Puoi pubblicare un'esperienza che consente ai giocatori di creare, personalizzare e acquistare corpi di avatar in tempo reale. Una volta acquistati, questi corpi personalizzati vengono salvati direttamente nell'inventario di Roblox del giocatore, consentendo ai giocatori di equipaggiare e indossare gli avatar personalizzati in altre esperienze.
I proprietari di esperienze che implementano la creazione di avatar in esperienza beneficiano delle commissioni del mercato sia come creatori dell'oggetto avatar che come proprietari dell'esperienza. Se un oggetto creato in esperienza viene ispezionato, questo fornisce un link all'esperienza originale in cui è stato creato.
Puoi testare la creazione in esperienza nella demo dell'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 di base Model che i giocatori possono modificare e personalizzare prima della pubblicazione.
Prima di iniziare, familiarizzati con quanto segue:
- Modelli di avatar — L'implementazione seguente richiede di importare un corpo di base che soddisfi le specifiche di 15 parti di Roblox. Questo Model funge da base per ulteriori personalizzazioni e modifiche degli utenti.
- Il corpo di base deve soddisfare le linee guida per i corpi avatar di Roblox, compreso il numero minimo di controlli FACS per il rigging facciale.
- Token di creazione avatar — Le esperienze che implementano la creazione di avatar richiedono almeno un token di creazione. Questi token richiedono Robux per essere acquistati e ti consentono di impostare prezzi e altre impostazioni di vendita per gli acquisti effettuati in esperienza.
- Classi API
- AvatarCreationService — Gestisce la richiesta di creazione avatar e la validazione.
- EditableImage — Gestisce la creazione e la manipolazione delle texture a runtime.
- EditableMesh — Gestisce la manipolazione della geometria della mesh a runtime.
- WrapDeformer — Gestisce la manipolazione della geometria della gabbia invisibile esterna che consente ai personaggi avatar di equipaggiare abbigliamento 3D.
Importa un corpo di base
Il corpo di base funge da fondazione iniziale che gli utenti possono personalizzare e modificare. Puoi utilizzare il tuo Model o importare un asset personalizzato con l'Importatore e configurarlo tramite la Configurazione Avatar.
I corpi di base devono aderire alle specifiche per avatar di Roblox e devono includere componenti come le 15 istanze MeshPart che compongono 6 parti del corpo: testa, torso, braccio sinistro, gamba sinistra, braccio destro e gamba destra, oltre ad altri componenti dell'avatar.
Per riferimenti e campioni di corpi avatar configurati correttamente, vedere Riferimenti avatar.
Implementa le API di editing
Per sviluppare un sistema in cui gli utenti possono modificare le istanze MeshPart su un avatar nella tua esperienza per la creazione, utilizza EditableImage per la modifica delle texture, EditableMesh per la modifica della mesh e WrapDeformer per mantenere i dati di skinning e FACS durante le modifiche alla mesh.
Dopo aver importato il tuo corpo di base, utilizza il seguente script per impostare i tuoi EditableImages, EditableMeshes e WrapDeformers.
local AssetService = game:GetService("AssetService")local function setupBodyPart(meshPart, wrapTarget)-- Crea e allega un WrapDeformer al MeshPartlocal wrapDeformer = Instance.new("WrapDeformer")wrapDeformer.Parent = meshPart-- Crea una mesh modificabile per la mesh gabbia del target di avvolgimentolocal cageEditableMesh: EditableMesh =AssetService:CreateEditableMeshAsync(Content.fromUri(wrapTarget.CageMeshId), {FixedSize = true,})-- Assegna la mesh gabbia al WrapDeformerwrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh))endlocal function setupRigidMesh(meshPart)-- Crea una mesh modificabile dall'originale MeshPartlocal editableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri(meshPart.MeshId), {FixedSize = true,})-- Genera un nuovo MeshPart dalla mesh modificabilelocal newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh))-- Copia dimensione, 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 esiste già un EditableImage 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 della texturelocal editableImage = AssetService:CreateEditableImageAsync(Content.fromUri(meshPart.TextureID))textureIdToEditableImageMap[meshPart.TextureID] = editableImagemeshPart.TextureContent = Content.fromObject(editableImage)endlocal function setupModel(model)-- Mappa per riutilizzare le istanze EditableImage per ID texturelocal textureIdToEditableImageMap = {}for _, descendant in model:GetDescendants() doif not descendant:IsA("MeshPart") thencontinueend-- Configura il MeshPart in base alla presenza del WrapTarget-- Se è presente WrapTarget, 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 ricolorare, disegnare o aggiungere adesivi al tuo corpo di base. Puoi sfruttare le API come DrawImage(), DrawRectangle(), WritePixelsBuffer().
Per trasformazioni avanzate, DrawImageTransformed() consente di specificare posizione, rotazione e scala quando si disegna un EditableImage su un altro. Allo stesso modo, DrawImageProjected() funziona molto come 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 della mesh sul tuo corpo.
WrapDeformer gestisce le deformazioni in tempo reale della geometria del MeshPart renderizzato mantenendo i dati di skinning e FACS sottostanti.
EditableMesh ti consente di modificare la mesh gabbia a cui il WrapDeformer risponde.
- Usa WrapDeformer:SetCageMeshContent() per applicare l'istanza EditableMesh che rappresenta la mesh 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
Crea un prompt di creazione
Dopo aver impostato il tuo corpo di base e le API di editing, crea un prompt per consentire agli utenti di creare e acquistare dall'esperienza utilizzando AvatarCreationService:PromptCreateAvatarAsync().
export type BodyPartInfo = {
bodyPart: Enum.BodyPart,
instance: Instance --Cartella con 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 avvenuto con successo con BundleId: ", bundleIdOrErrorMessage)
print("Caricamento avvenuto con successo con OutfitId: ", outfitId)
else
print("Caricamento non avvenuto con messaggio di errore:", bundleIdOrErrorMessage)
end
else
print("Creazione avatar non riuscita")
end
end
AvatarCreationService:PromptCreateAvatarAsync() accetta un parametro HumanoidDescription per rappresentare l'avatar destinato all'acquisto o alla creazione. Per la creazione avatar, il 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 accessorio nuovo di Hair.
Per supportare questo, il HumanoidDescription dovrebbe includere 6 figli 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 sul relevante Enum.BodyPart.
Ognuna delle 15 parti del corpo MeshPart deve includere:
- Un EditableImage.
- Un WrapDeformer con un EditableMesh.
Il HumanoidDescription fornito non dovrebbe includere alcun ID asset preesistente per rappresentare le parti del corpo o gli accessori sulla creazione prevista. D'altra parte, il HumanoidDescription può includere le scale umanoidi di BodyTypeScale, HeadScale, HeightScale, WidthScale, e ProportionScale. Fai attenzione alle scale con cui è importato un corpo di base affinché corrispondano alle scale fornite al HumanoidDescription.

Includi accessori
Se includi un accessorio, come i capelli, il HumanoidDescription dovrebbe includere un figlio AccessoryDescription in cui:
- La proprietà AccessoryDescription.Instance fa riferimento all'istanza Accessory.
- La proprietà AccessoryDescription.AccessoryType è impostata sul rilevante Enum.AccessoryType.
- Nel caso in cui includi 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.
Genera un token di creazione avatar
AvatarCreationService:PromptCreateAvatarAsync() accetta un parametro ID Token di Creazione Avatar. Questo token è la chiave per le creazioni dal tuo universo ed è quello che puoi usare per impostare il prezzo della creazione dell'avatar dalla tua esperienza. Per istruzioni e dettagli aggiuntivi sulla generazione dei token, vedi 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 poi utilizzare per il API AvatarCreationService:PromptCreateAvatarAsync().

Rispondi ai giocatori che si uniscono tramite attribuzione
I pacchetti avatar creati in esperienza includono un link di attribuzione all'esperienza originale in cui l'avatar è stato creato. Se l'avatar viene ispezionato da un altro giocatore, viene visualizzato un prompt che fornisce la possibilità di visitare l'esperienza in cui è stato creato l'avatar.

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