Sie können ein Erlebnis veröffentlichen, das es Spielern ermöglicht, Avatarkörper in Echtzeit zu erstellen, anzupassen und zu kaufen, zusätzlich zur Erstellung von Make-up im Erlebnis. Wenn diese benutzerdefinierten Items gekauft werden, werden sie direkt im Roblox-Inventar des Spielers gespeichert, sodass die Spieler sie in anderen Erlebnissen ausrüsten und tragen können.
Erlebnisbesitzer, die die Erstellung von Avataren im Erlebnis implementieren, profitieren von Marktplatzprovisionen als Verleger des Avatar-Items, während der Spieler, der es erstellt, der Ersteller des Items ist. Wenn ein im Erlebnis erstelltes Asset inspiziert wird, bietet das Item einen Link zum ursprünglichen Erlebnis, in dem es erstellt wurde.
Sie können die Erstellung im Erlebnis im Roblox Avatar Creator Demo testen.
So implementieren Sie die Erstellung im Erlebnis
Verwenden Sie die folgenden Anweisungen und Code-Referenzen, um Ihr erstes Projekt zur Erstellung von Avataren im Erlebnis zu erstellen. Die folgenden Anweisungen verwenden einen Basis-Körper Model, den die Spieler vor der Veröffentlichung modifizieren und anpassen können.
Bevor Sie beginnen, machen Sie sich mit Folgendem vertraut:
- Avatar-Modelle — Die folgende Implementierung erfordert den Import eines Basis-Körpers, der den 15-Teil-Spezifikationen von Roblox entspricht. Dieses Model dient als Grundlage für zusätzliche Benutzeranpassungen und -modifikationen.
- Der Basis-Körper muss den Avatar-Körperrichtlinien von Roblox entsprechen, einschließlich der Mindestanzahl an FACS-Steuerungen für die Gesichtsrigging.
- Avatar-Erstellungstoken — Erlebnisse, die die Avatar-Erstellung implementieren, benötigen mindestens ein Erstellungstoken. Diese Tokens erfordern Robux zum Kauf und ermöglichen es Ihnen, Preise und andere Verkaufsbedingungen für Käufe im Erlebnis festzulegen.
- API-Klassen
- AvatarCreationService — Verwaltet die Aufforderung zur Avatar-Erstellung und die Validierung.
- EditableImage — Verwaltet die Erstellung und Manipulation von Texturen zur Laufzeit.
- EditableMesh — Verwaltet die Manipulation der Mesh-Geometrie zur Laufzeit.
- WrapDeformer — Verwaltet die Manipulation der unsichtbaren äußeren Käfiggeometrie zur Laufzeit, die es Avatar-Charakteren ermöglicht, 3D-Kleidung auszurüsten.
Importieren eines Basis-Körpers
Der Basis-Körper dient als anfängliche Grundlage, die Benutzer anpassen und bearbeiten können. Sie können Ihr eigenes Model verwenden oder ein benutzerdefiniertes Asset mit dem Importer importieren und über Avatar Setup einrichten.
Basis-Körper müssen den Avatar-Spezifikationen von Roblox entsprechen und Komponenten wie die 15 MeshPart-Instanzen enthalten, die aus 6 Körperteilen bestehen: Kopf, Torso, linker Arm, linkes Bein, rechter Arm und rechtes Bein, sowie andere Avatar-Komponenten.
Für Referenzen und Beispiele für richtig konfigurierte Avatar-Körper siehe Avatar-Referenzen.
Implementieren von Bearbeitungs-APIs
Um ein System zu entwickeln, in dem Benutzer die MeshPart-Instanzen eines Avatars in Ihrem Erlebnis zur Erstellung bearbeiten können, verwenden Sie EditableImage für die Texturbearbeitung, EditableMesh für die Mesh-Bearbeitung und WrapDeformer, um das Skinning und die FACS-Daten während der Mesh-Bearbeitungen beizubehalten.
Nach dem Importieren Ihres Basis-Körpers verwenden Sie das folgende Skript, um Ihre EditableImages, EditableMeshes und WrapDeformers einzurichten.
local AssetService = game:GetService("AssetService")local function setupBodyPart(meshPart, wrapTarget)-- Erstellen und Anhängen eines WrapDeformers an das MeshPartlocal wrapDeformer = Instance.new("WrapDeformer")wrapDeformer.Parent = meshPart-- Erstellen eines bearbeitbaren Meshs für das Käfig-Mesh des Wrap-Zielslocal cageEditableMesh: EditableMesh =AssetService:CreateEditableMeshAsync(Content.fromUri(wrapTarget.CageMeshId), {FixedSize = true,})-- Zuweisen des Käfig-Meshs an den WrapDeformerwrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh))endlocal function setupRigidMesh(meshPart)-- Erstellen eines bearbeitbaren Meshs aus dem ursprünglichen MeshPartlocal editableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri(meshPart.MeshId), {FixedSize = true,})-- Generieren eines neuen MeshParts aus dem bearbeitbaren Meshlocal newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh))-- Kopieren von Größe, Position und Textur vom ursprünglichen MeshPartnewMeshPart.Size = meshPart.SizenewMeshPart.CFrame = meshPart.CFramenewMeshPart.TextureContent = meshPart.TextureContent-- Anwenden des neuen MeshParts auf das OriginalmeshPart:ApplyMesh(newMeshPart)endlocal function setupMeshTexture(meshPart, textureIdToEditableImageMap)-- Wenn bereits ein EditableImage für diese TextureID existiert, verwenden Sie es erneut, anstatt ein neues zu erstellenif textureIdToEditableImageMap[meshPart.TextureID] thenmeshPart.TextureContent =Content.fromObject(textureIdToEditableImageMap[meshPart.TextureID])returnend-- Erstellen eines neuen EditableImage und Anwenden als Texturinhaltlocal editableImage = AssetService:CreateEditableImageAsync(Content.fromUri(meshPart.TextureID))textureIdToEditableImageMap[meshPart.TextureID] = editableImagemeshPart.TextureContent = Content.fromObject(editableImage)endlocal function setupModel(model)-- Map zum Wiederverwenden von EditableImage-Instanzen nach Textur-IDlocal textureIdToEditableImageMap = {}for _, descendant in model:GetDescendants() doif not descendant:IsA("MeshPart") thencontinueend-- Konfigurieren des MeshPart basierend auf der WrapTarget-Präsenz-- Wenn WrapTarget vorhanden ist, fügen Sie ein WrapDeformer-Kind mit einem EditableMesh hinzu-- Andernfalls wenden Sie EditableMesh direkt auf das MeshPart anlocal wrapTarget = descendant:FindFirstChildOfClass("WrapTarget")if wrapTarget thensetupBodyPart(descendant, wrapTarget)elsesetupRigidMesh(descendant)end-- Konfigurieren des EditableImage für das MeshPartsetupMeshTexture(descendant, textureIdToEditableImageMap)endendErstellen Sie EditableImage-Tools, die es Spielern ermöglichen, Ihren Basis-Körper neu zu färben, zu zeichnen oder Aufkleber hinzuzufügen. Sie können APIs wie DrawImage(), DrawRectangle(), WritePixelsBuffer() nutzen.
Für fortgeschrittene Transformationen ermöglicht DrawImageTransformed(), dass Sie Position, Rotation und Skalierung beim Zeichnen eines EditableImage auf ein anderes angeben. Ähnlich funktioniert DrawImageProjected(), das wie DrawImage() funktioniert, aber das gezeichnete Bild korrekt projiziert, wenn die EditableImage-Instanz mit einem MeshPart verwendet wird.
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
Verwenden Sie WrapDeformer und EditableMesh, um Werkzeuge zur Bearbeitung von Mesh-Deformationen an Ihrem Körper zu erstellen.
WrapDeformer verwaltet die Live-Deformationen der gerenderten MeshPart-Geometrie und behält die zugrunde liegenden Skinning- und FACS-Daten bei.
EditableMesh ermöglicht es Ihnen, das Käfig-Mesh zu modifizieren, auf das der WrapDeformer reagiert.
- Verwenden Sie WrapDeformer:SetCageMeshContent(), um die EditableMesh-Instanz anzuwenden, die das relevante Käfig-Mesh für den WrapDeformer darstellt.
- Verwenden Sie EditableMesh, wie SetPosition(), um die Vertices zu deformieren und die Form des MeshPart zu bearbeiten.
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
Erstellen Sie eine Erstellungsaufforderung
Nachdem Sie Ihren Basis-Körper und die Bearbeitungs-APIs eingerichtet haben, erstellen Sie eine Aufforderung für Benutzer, um aus dem Erlebnis zu erstellen und zu kaufen, indem Sie AvatarCreationService:PromptCreateAvatarAsync() verwenden.
export type BodyPartInfo = {
bodyPart: Enum.BodyPart,
instance: Instance --Ordner mit erstellten MeshParts
}
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("Erfolgreich hochgeladen mit BundleId: ", bundleIdOrErrorMessage)
print("Erfolgreich hochgeladen mit OutfitId: ", outfitId)
else
print("Nicht erfolgreich hochgeladen mit Fehlermeldung:", bundleIdOrErrorMessage)
end
else
print("Avatar-Erstellung nicht erfolgreich")
end
endAvatarCreationService:PromptCreateAvatarAsync() benötigt einen HumanoidDescription-Parameter, um den Avatar darzustellen, der zum Kauf oder zur Erstellung vorgesehen ist. Für die Avatar-Erstellung muss die HumanoidDescription des Charakters neue Assets für jedes der 6 Körperteile enthalten (Head, Torso, RightLeg, LeftLeg, RightArm, LeftArm). Optional kann es auch ein neues Hair-Zubehör enthalten.
Um dies zu unterstützen, sollte die HumanoidDescription 6 BodyPartDescription-Kinder enthalten. Jede BodyPartDescription.Instance-Eigenschaft verweist auf einen Folder, der alle MeshPart-Instanzen enthält, die das Körperteil ausmachen. Zum Beispiel enthält der LeftArm-Ordner LeftHand, LeftUpperArm und LeftLowerArm MeshParts. Die BodyPartDescription.BodyPart-Eigenschaft sollte ebenfalls auf das relevante Enum.BodyPart gesetzt werden.
Jedes der 15 MeshPart-Körperteile muss Folgendes enthalten:
- Ein EditableImage.
- Ein WrapDeformer mit einem EditableMesh.
Die bereitgestellte HumanoidDescription sollte keine vorhandenen Asset-IDs enthalten, um Körperteile oder Zubehör bei der beabsichtigten Erstellung darzustellen. Andererseits kann die HumanoidDescription die humanoiden Skalen von BodyTypeScale, HeadScale, HeightScale, WidthScale und ProportionScale enthalten. Achten Sie darauf, dass die Skalen, mit denen ein Basis-Körper importiert wird, mit den Skalen übereinstimmen, die der HumanoidDescription bereitgestellt werden.

Zubehör einfügen
Wenn Sie ein Zubehör, wie z.B. Haare, einfügen, sollte die HumanoidDescription ein Kind AccessoryDescription enthalten, wo:
- Die AccessoryDescription.Instance-Eigenschaft verweist auf die Accessory-Instanz.
- Die AccessoryDescription.AccessoryType-Eigenschaft ist auf das relevante Enum.AccessoryType gesetzt.
- Im Falle der Einbeziehung von Enum.AccessoryType.Hair in Ihren Kreationen sollte das MeshPart ein EditableImage enthalten. Es sollte jedoch kein WrapDeformer-Kind enthalten, sondern ein EditableMesh, das direkt auf dem MeshPart gesetzt ist.
Generieren eines Avatar-Erstellungstokens
AvatarCreationService:PromptCreateAvatarAsync() benötigt einen Avatar-Erstellungstoken-ID-Parameter. Dieses Token ist der Schlüssel für Kreationen aus Ihrem Universum, und es ist das, was Sie verwenden können, um den Preis der Avatar-Erstellung aus Ihrem Erlebnis festzulegen. Für Anweisungen und zusätzliche Details zur Generierung von Tokens siehe Avatar-Erstellungstokens.
Nachdem Sie Ihr Token gekauft und generiert haben, können Sie das Token im Creator Hub inspizieren, um die ID zu finden, die Sie dann für die AvatarCreationService:PromptCreateAvatarAsync()-API verwenden können.

Auf Spieler reagieren, die durch Attribution beitreten
Avatar-Bundles, die im Erlebnis erstellt wurden, enthalten einen Attributionslink zum ursprünglichen Erlebnis, in dem der Avatar erstellt wurde. Wenn der Avatar von einem anderen Spieler inspiziert wird, wird eine Aufforderung angezeigt, die die Möglichkeit bietet, das Erlebnis zu besuchen, in dem der Avatar erstellt wurde.

Um Spieler zu behandeln, die Ihrem Erlebnis über diesen Attributionslink beitreten, verwenden Sie Player:GetJoinData() und analysieren Sie die zurückgegebene Tabelle nach GameJoinContext.
GameJoinContext enthält die folgenden Tabellenwerte:
- JoinSource — Enum.JoinSource
- Ein Player, der Ihrem Erlebnis über diesen Attributionslink beitritt, hat Enum.JoinSource.CreatedItemAttribution, um den Eintritt von einem erstellten Item anzuzeigen.
- ItemType — optional Enum.AvatarItemType
- AssetId — optional string
- OutfitId — optional string
- AssetType — optional Enum.AssetType