Présentation
Créer {itemType}
Monétiser
Politiques et directives

Création d'avatar en expérience

*Ce contenu est traduit en utilisant l'IA (Beta) et peut contenir des erreurs. Pour consulter cette page en anglais, clique ici.

Vous pouvez publier une expérience qui permet aux joueurs de créer, personnaliser et acheter des corps d'avatar en temps réel. Lorsqu'ils sont achetés, ces corps personnalisés sont directement enregistrés dans l'inventaire Roblox du joueur, permettant aux joueurs d'équiper et de porter les avatars personnalisés dans d'autres expériences.

Les propriétaires d'expériences qui mettent en œuvre la création d'avatar en expérience bénéficient des commissions de marché en tant que créateur de l'objet d'avatar et propriétaire d'expérience. Si un actif créé en expérience est inspecté, l'objet fournit un lien vers l'expérience d'origine dans laquelle il a été créé.

Vous pouvez tester la création en expérience dans la démo Avatar Creator de Roblox.

Comment mettre en œuvre la création en expérience

Utilisez les instructions et références de code suivantes pour créer votre premier projet de création d'avatar en expérience. Les instructions suivantes utilisent un corps de base Model que les joueurs peuvent modifier et personnaliser avant de publier.

Avant de commencer, familiarisez-vous avec les éléments suivants :

  • Modèles d'avatar — La mise en œuvre suivante nécessite l'importation d'un corps de base qui respecte les spécifications de 15 parties de Roblox. Ce Model sert de base pour une personnalisation et une modification supplémentaires par les utilisateurs.
  • Jetons de création d'avatar — Les expériences mettant en œuvre la création d'avatar nécessitent au moins un jeton de création. Ces jetons nécessitent des Robux pour les acheter et vous permettent de définir les prix et d'autres paramètres de vente pour les achats effectués en expérience.
  • Classes API
    • AvatarCreationService — Gère la demande de création d'avatar et la validation.
    • EditableImage — Gère la création et la manipulation des textures à l'exécution.
    • EditableMesh — Gère la manipulation de la géométrie des maillages à l'exécution.
    • WrapDeformer — Gère la manipulation en temps réel de la géométrie de cage extérieure invisible qui permet aux personnages d'avatar d'équiper des vêtements 3D.

Importer un corps de base

Le corps de base agit comme la fondation initiale que les utilisateurs peuvent personnaliser et modifier. Vous pouvez utiliser votre propre Model, ou importer un actif personnalisé avec l'Importateur et configurer via Avatar Setup.

Les corps de base doivent respecter les spécifications d'avatar de Roblox et doivent inclure des composants comme les 15 instances MeshPart qui composent 6 parties du corps : tête, torse, bras gauche, jambe gauche, bras droit et jambe droite, ainsi que d'autres composants d'avatar.

Pour des références et des échantillons de corps d'avatar correctement configurés, consultez Les références d'avatar.

Implementer les API d'édition

Pour développer un système où les utilisateurs peuvent éditer les instances MeshPart sur un avatar dans votre expérience pour la création, utilisez EditableImage pour l'édition des textures, EditableMesh pour l'édition des maillages, et WrapDeformer pour maintenir les données de peau et FACS pendant les modifications de maillage.

  1. Après avoir importé votre corps de base, utilisez le script suivant pour configurer vos EditableImages, EditableMeshes, et WrapDeformers.


    local AssetService = game:GetService("AssetService")
    local function setupBodyPart(meshPart, wrapTarget)
    -- Créer et attacher un WrapDeformer au MeshPart
    local wrapDeformer = Instance.new("WrapDeformer")
    wrapDeformer.Parent = meshPart
    -- Créer un maillage modifiable pour le maillage de cage de la cible de wrap
    local cageEditableMesh: EditableMesh =
    AssetService:CreateEditableMeshAsync(Content.fromUri(wrapTarget.CageMeshId), {
    FixedSize = true,
    })
    -- Assigner le maillage de cage au WrapDeformer
    wrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh))
    end
    local function setupRigidMesh(meshPart)
    -- Créer un maillage modifiable à partir du MeshPart d'origine
    local editableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri(meshPart.MeshId), {
    FixedSize = true,
    })
    -- Générer un nouveau MeshPart à partir du maillage modifiable
    local newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh))
    -- Copier la taille, la position et la texture du MeshPart d'origine
    newMeshPart.Size = meshPart.Size
    newMeshPart.CFrame = meshPart.CFrame
    newMeshPart.TextureContent = meshPart.TextureContent
    -- Appliquer le nouveau MeshPart à l'original
    meshPart:ApplyMesh(newMeshPart)
    end
    local function setupMeshTexture(meshPart, textureIdToEditableImageMap)
    -- Si EditableImage existe déjà pour ce TextureID, réutilisez-le plutôt que d'en créer un nouveau
    if textureIdToEditableImageMap[meshPart.TextureID] then
    meshPart.TextureContent =
    Content.fromObject(textureIdToEditableImageMap[meshPart.TextureID])
    return
    end
    -- Créer un nouvel EditableImage et l'appliquer comme contenu de texture
    local editableImage = AssetService:CreateEditableImageAsync(Content.fromUri(meshPart.TextureID))
    textureIdToEditableImageMap[meshPart.TextureID] = editableImage
    meshPart.TextureContent = Content.fromObject(editableImage)
    end
    local function setupModel(model)
    -- Mappage pour réutiliser les instances d'EditableImage par ID de texture
    local textureIdToEditableImageMap = {}
    for _, descendant in model:GetDescendants() do
    if not descendant:IsA("MeshPart") then
    continue
    end
    -- Configurer le MeshPart en fonction de la présence de WrapTarget
    -- Si WrapTarget est présent, ajouter un enfant WrapDeformer avec un EditableMesh
    -- Sinon, appliquer EditableMesh directement au MeshPart
    local wrapTarget = descendant:FindFirstChildOfClass("WrapTarget")
    if wrapTarget then
    setupBodyPart(descendant, wrapTarget)
    else
    setupRigidMesh(descendant)
    end
    -- Configurer l'EditableImage pour le MeshPart
    setupMeshTexture(descendant, textureIdToEditableImageMap)
    end
    end
  2. Créez des outils EditableImage qui permettent aux joueurs de recolorer, dessiner ou ajouter des autocollants à votre corps de base. Vous pouvez tirer parti des API comme DrawImage(), DrawRectangle(), WritePixelsBuffer().

    • Pour des transformations avancées, DrawImageTransformed() vous permet de spécifier la position, la rotation et l'échelle lors du dessin d'un EditableImage sur un autre. De même, DrawImageProjected() fonctionne de manière similaire à DrawImage() mais projette correctement l'image dessinée si l'instance EditableImage est utilisée avec 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. À l'aide de WrapDeformer et EditableMesh, créez des outils pour modifier les déformations de maillage sur votre corps.

    1. WrapDeformer gère les déformations en direct de la géométrie MeshPart rendue tout en maintenant les données de peau et FACS sous-jacentes.

    2. EditableMesh vous permet de modifier le maillage de cage auquel WrapDeformer répond.

      1. Utilisez WrapDeformer:SetCageMeshContent() pour appliquer l'instance EditableMesh qui représente le maillage de cage pertinent au WrapDeformer.
      2. Utilisez EditableMesh, tel que SetPosition(), pour déformer les sommets et éditer la forme du 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

Créer un prompt de création

Après avoir configuré votre corps de base et les API d'édition, créez un prompt pour que les utilisateurs puissent créer et acheter depuis l'expérience en utilisant AvatarCreationService:PromptCreateAvatarAsync().


export type BodyPartInfo = {
bodyPart: Enum.BodyPart,
instance: Instance -- Dossier contenant des MeshParts créés
}
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("Téléchargement réussi avec BundleId : ", bundleIdOrErrorMessage)
print("Téléchargement réussi avec OutfitId : ", outfitId)
else
print("Téléchargement échoué avec le message d'erreur :", bundleIdOrErrorMessage)
end
else
print("Création d'avatar échouée")
end
end

AvatarCreationService:PromptCreateAvatarAsync() prend un paramètre HumanoidDescription pour représenter l'avatar destiné à l'achat ou à la création. Pour la création d'avatar, la HumanoidDescription du personnage doit inclure de nouveaux actifs à créer pour chacune des 6 parties du corps (Head, Torso, RightLeg, LeftLeg, RightArm, LeftArm). En option, elle peut également inclure un nouvel accessoire de type Hair.

Pour soutenir cela, le HumanoidDescription doit inclure 6 enfants BodyPartDescription. Chaque propriété BodyPartDescription.Instance fait référence à un Folder qui contient toutes les instances de MeshPart qui composent la partie du corps. Par exemple, le dossier LeftArm contient LeftHand, LeftUpperArm, et LeftLowerArm MeshParts. La propriété BodyPartDescription.BodyPart doit également être définie sur l'Enum.BodyPart pertinent.

Chacune des 15 parties MeshPart doit inclure :

Le HumanoidDescription fourni ne doit inclure aucun ID d'actif préexistant pour représenter les parties du corps ou les accessoires sur la création prévue. D'autre part, le HumanoidDescription peut inclure les échelles humanoïdes de BodyTypeScale, HeadScale, HeightScale, WidthScale, et ProportionScale. Veuillez faire attention aux échelles avec lesquelles un corps de base est importé afin qu'elles correspondent aux échelles fournies au HumanoidDescription.

Inclure des accessoires

Si vous incluez un accessoire, tel que des cheveux, la HumanoidDescription doit inclure un enfant AccessoryDescription où :

Générer un jeton de création d'avatar

AvatarCreationService:PromptCreateAvatarAsync() prend un paramètre ID de jeton de création d'avatar. Ce jeton est la clé pour les créations de votre univers, et c'est ce que vous pouvez utiliser pour définir le prix de la création d'avatar depuis votre expérience. Pour des instructions et des détails supplémentaires sur la génération de jetons, consultez Jetons de création d'avatar.

Après avoir acheté et généré votre jeton, vous pouvez inspecter le jeton dans le Creator Hub pour trouver l'ID que vous pouvez ensuite utiliser pour l'API AvatarCreationService:PromptCreateAvatarAsync().

Répondre aux joueurs qui rejoignent par attribution

Les paquets d'avatar créés en expérience incluent un lien d'attribution vers l'expérience d'origine dans laquelle l'avatar a été créé. Si l'avatar est inspecté par un autre joueur, une invite s'affiche offrant la possibilité de visiter l'expérience où l'avatar a été créé.

Pour gérer les joueurs rejoignant votre expérience en utilisant ce lien d'attribution, utilisez Player:GetJoinData() et parsez la table retournée pour GameJoinContext.

GameJoinContext inclut les valeurs de table suivantes :

©2026 Société Roblox. Roblox, le logo Roblox et Powering Imagination font partie de nos marques déposées aux États-Unis et dans d'autres pays.