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.
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 MeshPartlocal wrapDeformer = Instance.new("WrapDeformer")wrapDeformer.Parent = meshPart-- Créer un maillage modifiable pour le maillage de la cage de la cible enveloppantelocal cageEditableMesh: EditableMesh =AssetService:CreateEditableMeshAsync(Content.fromUri(wrapTarget.CageMeshId), {FixedSize = true,})-- Attribuez la maille de la cage au WrapDeformerwrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh))endlocal function setupRigidMesh(meshPart)-- Créer un maillage modifiable à partir de la partie maillage originalelocal editableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri(meshPart.MeshId), {FixedSize = true,})-- Générer une nouvelle partie maillée à partir du maillage modifiablelocal newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh))-- Copier la taille, la position et la texture de la partie maillée originalenewMeshPart.Size = meshPart.SizenewMeshPart.CFrame = meshPart.CFramenewMeshPart.TextureContent = meshPart.TextureContent-- Appliquer la nouvelle partie maillée à l'originalmeshPart:ApplyMesh(newMeshPart)endlocal function setupMeshTexture(meshPart, textureIdToEditableImageMap)-- Si EditableImage existe déjà pour cet ID de texture, réutilisez-le plutôt que de créer un nouveauif textureIdToEditableImageMap[meshPart.TextureID] thenmeshPart.TextureContent =Content.fromObject(textureIdToEditableImageMap[meshPart.TextureID])returnend-- Créer une nouvelle image modifiable et l'appliquer comme contenu de texturelocal editableImage = AssetService:CreateEditableImageAsync(Content.fromUri(meshPart.TextureID))textureIdToEditableImageMap[meshPart.TextureID] = editableImagemeshPart.TextureContent = Content.fromObject(editableImage)endlocal function setupModel(model)-- Carte pour réutiliser les instances EditableImage par ID de texturelocal textureIdToEditableImageMap = {}for _, descendant in model:GetDescendants() doif not descendant:IsA("MeshPart") thencontinueend-- 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éelocal wrapTarget = descendant:FindFirstChildOfClass("WrapTarget")if wrapTarget thensetupBodyPart(descendant, wrapTarget)elsesetupRigidMesh(descendant)end-- Configurer l'image modifiable pour la partie mailléesetupMeshTexture(descendant, textureIdToEditableImageMap)endendCré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)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 = (game.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
En utilisant WrapDeformer et EditableMesh, créez des outils pour modifier les déformations de maillage sur votre corps.
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.
EditableMesh vous permet de modifier le maillage de la cage auquel la WrapDeformer répond.
- Utilisez WrapDeformer:SetCageMeshContent() pour appliquer l'instance EditableMesh qui représente le maillage de cage pertinent au WrapDeformer.
- 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.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
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 :
- An EditableImage .
- Un WrapDeformer avec un EditableMesh.
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ù :
- La propriété AccessoryDescription.Instance réfère à l'instance Accessory.
- La propriété AccessoryDescription.AccessoryType est définie sur la valeur pertinente Enum.AccessoryType.
- Dans le cas d'inclusion de Enum.AccessoryType.Hair dans vos créations, le MeshPart devrait inclure un EditableImage .Cependant, il ne devrait pas inclure un enfant WrapDeformer et devrait inclure un EditableMesh ensemble sur le MeshPart directement.
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 :
- JoinSource — Enum.JoinSource
- Rejoindre votre expérience à partir de ce lien d'attribution Player aura Enum.JoinSource.CreatedItemAttribution pour indiquer l'entrée d'un itemcréé.
- ItemType — optionnel Enum.AvatarItemType
- AssetId — optionnel string
- OutfitId — optionnel string
- AssetType — optionnel Enum.AssetType