créationsd'avatar dans l'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, de personnaliser et d'acheter des corps d'avatar en temps réel.Lorsqu'ils sont achetés, ces corps personnalisés sont enregistrés directement dans l'inventaire Roblox du joueur, ce qui permet aux joueurs d'équiper et de porter les avatars personnalisés dans d'autres expériences.

Les propriétaires d'expérience qui implémentent la création d'avatar dans l'expérience bénéficient de commissions du marché en tant que créateur de l'élément d'avatar et en tant que propriétaire d'expérience .Si une ressource créée dans l'expérience est inspectée, l'élément fournit un lien vers l'expérience originale dans laquelle elle a été créée.

Vous pouvez tester la création en expérience dans la démo créateur d'avatar de Roblox.

Comment mettre en œuvre la créationsdans l'expérience

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

Avant de commencer, familiarisez-vous avec ce qui suivre:

  • Modèles d'avatar — La mise en œuvre suivante nécessite l'importation d'un corps de base qui répond aux spécifications de 15 parties de Roblox.Ceci Model sert de base pour la personnalisation et la modification supplémentaires de l'utilisateur.
    • Le corps de base doit répondre aux directives du corps d'avatar de Roblox Avatar body guidelines, y compris le nombre minimum de contrôle FACS pour le rigging structuréfacial.
  • 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 être achetés et vous permettent de définir des prix et d'autres paramètres de vente pour les achats effectués dans l'expérience.
  • Classes d'API
    • AvatarCreationService — Gère la création d'un avatar incitant à la validation.
    • EditableImage — Gère la création et la manipulation de l'exécution des textures.
    • EditableMesh — Gère la manipulation en temps d'exécution de la géométrie du maillage.
    • WrapDeformer — Gère la manipulation en temps d'exécution de la géométrie de la cage extérieure invisible qui permet aux personnages d'avatar d'équiper 3D vêtements.

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 une ressource personnalisée avec le Importer 3D et configurer via l'installation d'Avatar .

Les corps de base doivent adhérer aux spécifications d'avatar de Roblox et inclure des composants tels que les 15 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 avatar.

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

Implémenter des API d'édition

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

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


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

    • Pour les transformations avancées, DrawImageTransformed() vous permet de spécifier la position, la rotation et l'échelle lors du dessin d'une image modifiable sur une autre.De même, DrawImageProjected() fonctionne de manière similaire à DrawImage() mais projette l'image dessinée correctement 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 = (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. En utilisant 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 rendue MeshPart tout en maintenant les données de skinning et de FACS sous-jacentes.

    2. EditableMesh vous permet de modifier le maillage de la cage auquel la 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, comme SetPosition(), pour déformer les sommets et modifier 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 une invite de création

Après avoir configuré votre corps de base et édité des API, créez une demande pour les utilisateurs de créer et d'acheter à partir de l'expérience en utilisant AvatarCreationService:PromptCreateAvatarAsync() .


export type BodyPartInfo = {
bodyPart: Enum.BodyPart,
instance: Instance --Dossier avec pièces de maillage créées
}
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() prend un paramètre HumanoidDescription pour représenter l'avatar destiné à l'achat ou à la créations.Pour la créationsd'un avatar, les caractères de HumanoidDescription doivent inclure de nouvelles ressources à créer pour chacune des 6 parties du corps ( Head , Torso , RightLeg , LeftLeg , RightArm , LeftArm ).Facultativement, il peut également inclure un nouvel accesoireHair .

Pour soutenir cela, le HumanoidDescription devrait inclure 6 BodyPartDescription enfants.Chaque propriété BodyPartDescription.Instance réfère à une Folder qui inclut toutes les instances MeshPart qui composent la partie du corps.Par exemple, le dossier LeftArm contient LeftHand , LeftUpperArm , et LeftLowerArm``Class.MeshPart|MeshParts .La propriété BodyPartDescription.BodyPart doit également être définie sur le Enum.BodyPart pertinent.

Chacune des parties du corps 15 MeshPart doit inclure :

L'ID de ressource fournie HumanoidDescription ne doit pas inclure d'ID de ressource préexistante pour représenter des parties du corps ou des accessoires de la créationsprévue.D'un autre côté, le HumanoidDescription peut inclure les écailles humanoïdes de BodyTypeScale , HeadScale , HeightScale , WidthScale et ProportionScale .Gardez à l'esprit les échelles avec lesquelles un corps de base est importé afin qu'elles correspondent aux échelles fournies à la HumanoidDescription .

Inclure des accessoires

Si vous incluez un accesoire, comme des cheveux, le HumanoidDescription devrait inclure un enfant AccessoryDescription où :

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

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

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

Répondre aux joueurs qui se joignent par attribution

Les forfaits d'avatar créés dans l'expérience incluent un lien d'attribution vers l'expérience originale à laquelle l'avatar a été créé.Si l'avatar est inspecté par un autre joueur, une demande s'affiche pour fournir une option de visiter l'expérience où l'avatar a été créé.

Pour gérer les joueurs qui rejoignent votre expérience en utilisant ce lierd'attribution, utilisez Player:GetJoinData() et analysez la table retournée pour GameJoinContext.

GameJoinContext inclut les valeurs de table suivantes :