Puoi pubblicare un'esperienza che consenta ai giocatori di creare, personalizzare e acquistare corpi avatar in tempo reale. Quando vengono acquistati, questi corpi personalizzati vengono salvati direttamente nell'inventario Roblox del giocatore, consentendo ai giocatori di equipaggiare e indossare gli avatar personalizzati in altre esperienze.
I proprietari dell'esperienza che implementano la creazione di avatar in esperienza beneficiano delle commissioni di mercato sia come creatore dell'oggetto avatar che come proprietario dell'esperienza. Se un asset creato nell'esperienza viene ispezionato, l'oggetto fornisce un collegamento all'esperienza originale in cui è stato creato.
Puoi testare la creazione in esperienza nella demo Avatar Creator di Roblox.
Come implementare la creazione in esperienza
Utilizza le seguenti istruzioni e riferimenti di codice per creare il tuo primo progetto di creazione avatar in esperienza. Le seguenti istruzioni utilizzano un corpo base Model che i giocatori possono modificare e personalizzare prima della pubblicazione.
Prima di iniziare, familiarizza con i seguenti aspetti:
- Modelli avatar — La seguente implementazione richiede l'importazione di un corpo base che soddisfi le specifiche di 15 parti di Roblox. Questo Model funge da base per ulteriore personalizzazione e modifica da parte degli utenti.
- Il corpo base deve soddisfare le linee guida sui corpi avatar di Roblox, inclusi il numero minimo di controlli FACS per il rigging facciale.
- Token di creazione avatar — Le esperienze che implementano la creazione avatar richiedono almeno un token di creazione. Questi token richiedono Robux per l'acquisto e ti consentono di impostare prezzi e altre impostazioni di vendita per gli acquisti effettuati nell'esperienza.
- Classi API
- AvatarCreationService — Gestisce la richiesta di creazione avatar e la validazione.
- EditableImage — Gestisce la creazione e la manipolazione runtime delle texture.
- EditableMesh — Gestisce la manipolazione runtime della geometria dei mesh.
- WrapDeformer — Gestisce la manipolazione runtime della geometria della gabbia esterna invisibile che consente ai personaggi avatar di equipaggiare abbigliamento 3D.
Importare un corpo base
Il corpo base funge da fondamento iniziale che gli utenti possono personalizzare e modificare. Puoi utilizzare il tuo Model o importare un asset personalizzato con l'Importatore e configurarlo tramite Avatar Setup.
I corpi base devono rispettare le specifiche dell'avatar di Roblox e devono includere componenti come le 15 istanze di MeshPart che compongono 6 parti del corpo: testa, torso, braccio sinistro, gamba sinistra, braccio destro e gamba destra, così come altre componenti avatar.
Per riferimenti e campioni di corpi avatar configurati correttamente, vedi Riferimenti avatar.
Implementare le API di modifica
Per sviluppare un sistema in cui gli utenti possono modificare le istanze di MeshPart su un avatar nella tua esperienza per la creazione, utilizza EditableImage per la modifica delle texture, EditableMesh per la modifica del mesh e WrapDeformer per mantenere i dati di skinning e FACS durante le modifiche del mesh.
Dopo aver importato il tuo corpo base, utilizza il seguente script per configurare il tuo EditableImages, EditableMeshes e WrapDeformers.
local AssetService = game:GetService("AssetService")local function setupBodyPart(meshPart, wrapTarget)-- Crea e attacca un WrapDeformer al MeshPartlocal wrapDeformer = Instance.new("WrapDeformer")wrapDeformer.Parent = meshPart-- Crea un mesh modificabile per il mesh della gabbia del target di avvolgimentolocal cageEditableMesh: EditableMesh =AssetService:CreateEditableMeshAsync(Content.fromUri(wrapTarget.CageMeshId), {FixedSize = true,})-- Assegna il mesh della gabbia al WrapDeformerwrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh))endlocal function setupRigidMesh(meshPart)-- Crea un mesh modificabile dall'originale MeshPartlocal editableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri(meshPart.MeshId), {FixedSize = true,})-- Genera un nuovo MeshPart dal mesh modificabilelocal newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh))-- Copia dimensioni, posizione e texture dall'originale MeshPartnewMeshPart.Size = meshPart.SizenewMeshPart.CFrame = meshPart.CFramenewMeshPart.TextureContent = meshPart.TextureContent-- Applica il nuovo MeshPart all'originalemeshPart:ApplyMesh(newMeshPart)endlocal function setupMeshTexture(meshPart, textureIdToEditableImageMap)-- Se EditableImage esiste già per questo TextureID, riutilizzalo piuttosto che crearne uno nuovoif textureIdToEditableImageMap[meshPart.TextureID] thenmeshPart.TextureContent =Content.fromObject(textureIdToEditableImageMap[meshPart.TextureID])returnend-- Crea un nuovo EditableImage e applicalo come contenuto texturelocal editableImage = AssetService:CreateEditableImageAsync(Content.fromUri(meshPart.TextureID))textureIdToEditableImageMap[meshPart.TextureID] = editableImagemeshPart.TextureContent = Content.fromObject(editableImage)endlocal function setupModel(model)-- Mappa per riutilizzare istanze di EditableImage per ID texturelocal textureIdToEditableImageMap = {}for _, descendant in model:GetDescendants() doif not descendant:IsA("MeshPart") thencontinueend-- Configura il MeshPart in base alla presenza di WrapTarget-- Se WrapTarget è presente, aggiungi un figlio WrapDeformer con un EditableMesh-- Altrimenti, applica EditableMesh direttamente al MeshPartlocal wrapTarget = descendant:FindFirstChildOfClass("WrapTarget")if wrapTarget thensetupBodyPart(descendant, wrapTarget)elsesetupRigidMesh(descendant)end-- Configura l'EditableImage per il MeshPartsetupMeshTexture(descendant, textureIdToEditableImageMap)endendCrea strumenti EditableImage che consentano ai giocatori di cambiare colore, disegnare o aggiungere adesivi al tuo corpo base. Puoi sfruttare le API come DrawImage(), DrawRectangle(), WritePixelsBuffer().
Per trasformazioni avanzate, DrawImageTransformed() ti consente di specificare posizione, rotazione e scala quando disegni un EditableImage su un altro. Analogamente, DrawImageProjected() funziona molto simile a DrawImage(), ma proietta correttamente l'immagine disegnata se l'istanza EditableImage è utilizzata 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)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 = (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
Utilizzando WrapDeformer e EditableMesh, crea strumenti per modificare le deformazioni del mesh sul tuo corpo.
WrapDeformer gestisce le deformazioni in tempo reale della geometria renderizzata di MeshPart, mantenendo i dati di skinning e FACS sottostanti.
EditableMesh ti consente di modificare il mesh della gabbia a cui risponde WrapDeformer.
- Usa WrapDeformer:SetCageMeshContent() per applicare l'istanza EditableMesh che rappresenta il mesh della gabbia pertinente al WrapDeformer.
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
Creare il prompt di creazione
Dopo aver configurato il tuo corpo base e le API di modifica, crea un prompt per gli utenti per creare e acquistare dall'esperienza utilizzando AvatarCreationService:PromptCreateAvatarAsync().
export type BodyPartInfo = {
bodyPart: Enum.BodyPart,
instance: Instance --Cartella con i MeshParts creati
}
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("Caricamento riuscito con BundleId: ", bundleIdOrErrorMessage)
print("Caricamento riuscito con OutfitId: ", outfitId)
else
print("Caricamento non riuscito con messaggio di errore:", bundleIdOrErrorMessage)
end
else
print("Creazione avatar non riuscita")
end
endAvatarCreationService:PromptCreateAvatarAsync() prende un parametro HumanoidDescription per rappresentare l'avatar destinato all'acquisto o alla creazione. Per la creazione dell'avatar, la HumanoidDescription del personaggio deve includere nuovi asset da creare per ciascuna delle 6 parti del corpo (Head, Torso, RightLeg, LeftLeg, RightArm, LeftArm). Facoltativamente, può anche includere un nuovo accessorio Hair.
Per supportare ciò, la HumanoidDescription dovrebbe includere 6 bambini di BodyPartDescription. Ogni proprietà BodyPartDescription.Instance fa riferimento a una Folder che include tutte le istanze di MeshPart che compongono la parte del corpo. Ad esempio, la cartella LeftArm contiene LeftHand, LeftUpperArm e LeftLowerArm MeshParts. La proprietà BodyPartDescription.BodyPart dovrebbe anche essere impostata sulla relativa Enum.BodyPart.
Ciascuna delle 15 parti del corpo MeshPart deve includere:
- Un EditableImage.
- Un WrapDeformer con un EditableMesh.
La HumanoidDescription fornita non dovrebbe includere alcun ID di asset preesistenti per rappresentare le parti del corpo o gli accessori sulla creazione destinata. D'altra parte, la HumanoidDescription può includere le scale umane di BodyTypeScale, HeadScale, HeightScale, WidthScale, e ProportionScale. Fai attenzione alle scale con cui un corpo base è importato affinché corrispondano alle scale fornite alla HumanoidDescription.

Includere accessori
Se stai includendo un accessorio, come capelli, la HumanoidDescription dovrebbe includere una descrizione di accessorio figlio AccessoryDescription dove:
- La proprietà AccessoryDescription.Instance fa riferimento all'istanza Accessory.
- La proprietà AccessoryDescription.AccessoryType è impostata sulla relativa Enum.AccessoryType.
- Nel caso in cui stai includendo Enum.AccessoryType.Hair nelle tue creazioni, il MeshPart dovrebbe includere un EditableImage. Tuttavia, non dovrebbe includere un figlio WrapDeformer, ma dovrebbe includere un EditableMesh impostato direttamente sul MeshPart.
Generare un token di creazione avatar
AvatarCreationService:PromptCreateAvatarAsync() prende un parametro ID Token di Creazione Avatar. Questo token è la chiave per le creazioni dal tuo universo e può essere utilizzato per impostare il prezzo della creazione di avatar dalla tua esperienza. Per istruzioni e dettagli aggiuntivi sulla generazione di token, consulta Token di Creazione Avatar.
Dopo aver acquistato e generato il tuo token, puoi ispezionare il token nel Creator Hub per trovare l'ID che puoi quindi utilizzare per l'API AvatarCreationService:PromptCreateAvatarAsync().

Rispondere all'ingresso dei giocatori tramite attribuzione
I pacchetti avatar creati in esperienza includono un collegamento di attribuzione all'esperienza originale in cui è stato creato l'avatar. Se l'avatar viene ispezionato da un altro giocatore, viene visualizzato un prompt che offre un'opzione per visitare l'esperienza in cui è stato creato l'avatar.

Per gestire l'ingresso dei giocatori nella tua esperienza utilizzando questo collegamento di attribuzione, utilizza Player:GetJoinData() e analizza la tabella restituita per GameJoinContext.
GameJoinContext include i seguenti valori di tabella:
- JoinSource — Enum.JoinSource
- Un Player che entra nella tua esperienza tramite questo collegamento di attribuzione avrà Enum.JoinSource.CreatedItemAttribution per indicare l'ingresso da un oggetto creato.
- ItemType — Enum.AvatarItemType opzionale
- AssetId — string opzionale
- OutfitId — string opzionale
- AssetType — Enum.AssetType opzionale