Możesz opublikować doświadczenie, które pozwala graczom tworzyć, dostosowywać i kupować ciała awatarów w czasie rzeczywistym. Po zakupie, te niestandardowe ciała są bezpośrednio zapisywane w magazynie Robloxa gracza, co pozwala graczom zakładać i nosić niestandardowe awatary w innych doświadczeniach.
Właściciele doświadczeń, którzy wdrożą tworzenie awatarów w ich doświadczeniach, odnoszą korzyści z prowizji na rynku zarówno jako twórcy przedmiotu awatara, jak i właściciele doświadczenia. Jeśli zasób stworzony w doświadczeniu zostanie sprawdzony, przedmiot dostarcza link do oryginalnego doświadczenia, w którym został stworzony.
Możesz przetestować tworzenie awatarów w Avatar Creator Robloxa.
Jak wdrożyć tworzenie awatarów w doświadczeniu
Użyj następujących instrukcji i odniesień do kodu, aby stworzyć swój pierwszy projekt tworzenia awatarów w doświadczeniu. Poniższe instrukcje wykorzystują podstawowe ciało Model, które gracze mogą modyfikować i dostosowywać przed publikowaniem.
Przed przystąpieniem do pracy, zapoznaj się z następującymi zagadnieniami:
- Modele awatarów — Następna implementacja wymaga importowania podstawowego ciała, które spełnia 15-specyfikacyjne wymagania Robloxa. To Model służy jako baza dla dodatkowej personalizacji i modyfikacji przez użytkowników.
- Podstawowe ciało musi spełniać wytyczne dotyczące ciał awatarów Robloxa, w tym minimalną liczbę kontrol FACS dla rigowania twarzy.
- Tokeny do tworzenia awatarów — Doświadczenia wdrażające tworzenie awatarów wymagają co najmniej jednego tokena do tworzenia. Te tokeny wymagają Robux do zakupu i pozwalają na ustalanie cen oraz innych ustawień sprzedaży dla zakupów dokonywanych w doświadczeniu.
- Klasy API
- AvatarCreationService — Obsługuje zapytania o tworzenie awatarów oraz walidację.
- EditableImage — Obsługuje runtime tworzenie i manipulację teksturami.
- EditableMesh — Obsługuje runtime manipulację geometrią siatki.
- WrapDeformer — Obsługuje runtime manipulację niewidoczną geometrią zewnętrznej klatki, która pozwala postaciom awatarów zakładać 3D odzież.
Importowanie podstawowego ciała
Podstawowe ciało działa jako podstawowa baza, którą użytkownicy mogą dostosowywać i edytować. Możesz użyć własnego Model lub zaimportować niestandardowy zasób za pomocą Importera oraz skonfigurować przez Konfigurację Awatara.
Podstawowe ciała muszą przestrzegać specyfikacji awatarów Robloxa i muszą zawierać komponenty, takie jak 15 instancji MeshPart, które składają się z 6 części ciała: głowa, tors, lewa ręka, lewa noga, prawa ręka i prawa noga, a także inne komponenty awatara.
Dla odniesień i prób prawidłowo skonfigurowanych ciał awatarów, zobacz Odniesienia do awatarów.
Wdrożenie edytowanych API
Aby opracować system, w którym użytkownicy mogą edytować instancje MeshPart na awatarze w Twoim doświadczeniu do tworzenia, użyj EditableImage do edytowania tekstur, EditableMesh do edytowania siatki i WrapDeformer do zachowania danych skór i FACS podczas edytowania siatki.
Po zaimportowaniu podstawowego ciała, użyj następującego skryptu, aby skonfigurować swoje EditableImages, EditableMeshes oraz WrapDeformers.
local AssetService = game:GetService("AssetService")local function setupBodyPart(meshPart, wrapTarget)-- Utwórz i dołącz WrapDeformer do MeshPartlocal wrapDeformer = Instance.new("WrapDeformer")wrapDeformer.Parent = meshPart-- Utwórz edytowalną siatkę dla siatki klatki docelowejlocal cageEditableMesh: EditableMesh =AssetService:CreateEditableMeshAsync(Content.fromUri(wrapTarget.CageMeshId), {FixedSize = true,})-- Przypisz siatkę klatki do WrapDeformerwrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh))endlocal function setupRigidMesh(meshPart)-- Utwórz edytowalną siatkę z oryginalnego MeshPartlocal editableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri(meshPart.MeshId), {FixedSize = true,})-- Generuj nowy MeshPart z edytowalnej siatkilocal newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh))-- Skopiuj rozmiar, pozycję i teksturę z oryginalnego MeshPartnewMeshPart.Size = meshPart.SizenewMeshPart.CFrame = meshPart.CFramenewMeshPart.TextureContent = meshPart.TextureContent-- Zastosuj nowy MeshPart do oryginalnegomeshPart:ApplyMesh(newMeshPart)endlocal function setupMeshTexture(meshPart, textureIdToEditableImageMap)-- Jeśli EditableImage już istnieje dla tego TextureID, użyj go ponownie zamiast tworzyć nowegoif textureIdToEditableImageMap[meshPart.TextureID] thenmeshPart.TextureContent =Content.fromObject(textureIdToEditableImageMap[meshPart.TextureID])returnend-- Utwórz nowy EditableImage i zastosuj go jako zawartość teksturylocal editableImage = AssetService:CreateEditableImageAsync(Content.fromUri(meshPart.TextureID))textureIdToEditableImageMap[meshPart.TextureID] = editableImagemeshPart.TextureContent = Content.fromObject(editableImage)endlocal function setupModel(model)-- Mapowanie do ponownego użycia instancji EditableImage według ID teksturylocal textureIdToEditableImageMap = {}for _, descendant in model:GetDescendants() doif not descendant:IsA("MeshPart") thencontinueend-- Skonfiguruj MeshPart w zależności od obecności WrapTarget-- Jeśli WrapTarget jest obecny, dodaj dziecko WrapDeformer z EditableMesh-- W przeciwnym razie, zastosuj EditableMesh bezpośrednio do MeshPartlocal wrapTarget = descendant:FindFirstChildOfClass("WrapTarget")if wrapTarget thensetupBodyPart(descendant, wrapTarget)elsesetupRigidMesh(descendant)end-- Skonfiguruj EditableImage dla MeshPartsetupMeshTexture(descendant, textureIdToEditableImageMap)endendStwórz narzędzia EditableImage, które pozwalają graczom na zmianę kolorów, rysowanie lub dodawanie naklejek do twojego podstawowego ciała. Możesz wykorzystać API, takie jak DrawImage(), DrawRectangle(), WritePixelsBuffer().
Dla zaawansowanych transformacji, DrawImageTransformed() pozwala określić pozycję, rotację i skalę przy rysowaniu jednego EditableImage na drugi. Podobnie, DrawImageProjected() działa podobnie jak DrawImage(), ale odpowiednio projektuje narysowany obraz, jeśli instancja EditableImage jest używana z 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
Używając WrapDeformer i EditableMesh, stwórz narzędzia do edytowania deformacji siatki na twoim ciele.
WrapDeformer obsługuje na żywo deformacje renderowanej geometrii MeshPart, zachowując dane o skórze i FACS.
EditableMesh pozwala na modyfikację siatki klatki, na którą reaguje WrapDeformer.
- Użyj WrapDeformer:SetCageMeshContent(), aby zastosować instancję EditableMesh, która reprezentuje odpowiednią siatkę klatki do WrapDeformer.
- Użyj EditableMesh, takich jak SetPosition(), aby zdeformować wierzchołki i edytować kształt 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
Utwórz prompt do tworzenia
Po skonfigurowaniu podstawowego ciała i API edytowania, stwórz prompt dla użytkowników, aby mogli tworzyć i kupować z doświadczenia, używając AvatarCreationService:PromptCreateAvatarAsync().
export type BodyPartInfo = {
bodyPart: Enum.BodyPart,
instance: Instance --Folder z utworzonymi 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("Pomyślnie przesłano z BundleId: ", bundleIdOrErrorMessage)
print("Pomyślnie przesłano z OutfitId: ", outfitId)
else
print("Nieudane przesłanie z komunikatem o błędzie:", bundleIdOrErrorMessage)
end
else
print("Tworzenie awatara nieudane")
end
endAvatarCreationService:PromptCreateAvatarAsync() przyjmuje parametr HumanoidDescription, aby reprezentować awatar zamierzony do zakupu lub stworzenia. W przypadku tworzenia awatara, HumanoidDescription postaci musi zawierać nowe zasoby do utworzenia dla każdej z 6 części ciała (Head, Torso, RightLeg, LeftLeg, RightArm, LeftArm). Opcjonalnie może również zawierać nowy dodatek Hair.
Aby to wspierać, HumanoidDescription powinien zawierać 6 dzieci BodyPartDescription. Każda właściwość BodyPartDescription.Instance odnosi się do Folder, która zawiera wszystkie instancje MeshPart, które składają się na część ciała. Na przykład, folder LeftArm zawiera LeftHand, LeftUpperArm i LeftLowerArm MeshParts. Właściwość BodyPartDescription.BodyPart powinna również być ustawiona na odpowiedni Enum.BodyPart.
Każda z 15 części ciała MeshPart musi zawierać:
Dostarczony HumanoidDescription nie powinien zawierać żadnych istniejących identyfikatorów zasobów, aby reprezentować części ciała lub akcesoria na zamierzonym tworzeniu. Z drugiej strony, HumanoidDescription może zawierać skale humanoida BodyTypeScale, HeadScale, HeightScale, WidthScale, i ProportionScale. Zwróć uwagę na skale, z jakimi importowane jest podstawowe ciało, aby pasowały do skali podanej w HumanoidDescription.

Uwzględnij akcesoria
Jeśli uwzględniasz akcesorium, takie jak włosy, HumanoidDescription powinien zawierać dziecko AccessoryDescription, gdzie:
- Właściwość AccessoryDescription.Instance odnosi się do instancji Accessory.
- Właściwość AccessoryDescription.AccessoryType jest ustawiona na odpowiedni Enum.AccessoryType.
- W przypadku uwzględnienia Enum.AccessoryType.Hair w swoich tworzeniach, MeshPart powinien zawierać EditableImage. Jednak nie powinien zawierać dziecka WrapDeformer, lecz powinien zawierać EditableMesh ustawiony bezpośrednio na MeshPart.
Generuj token do tworzenia awatara
AvatarCreationService:PromptCreateAvatarAsync() przyjmuje parametr ID tokena do tworzenia awatara. Ten token jest kluczem do tworzeń z twojego uniwersum i można go użyć do ustawienia ceny tworzenia awatara z twojego doświadczenia. Po szczegóły i dodatkowe informacje na temat generowania tokenów, zobacz Tokeny do tworzenia awatarów.
Po zakupie i wygenerowaniu tokena, możesz sprawdzić token w Centrum Twórców, aby znaleźć ID, które następnie możesz wykorzystać w API AvatarCreationService:PromptCreateAvatarAsync().

Reaguj na graczy dołączających przez atrybucję
Pakiety awatarów stworzone w doświadczeniu zawierają link atrybucji do oryginalnego doświadczenia, w którym został stworzony awatar. Jeśli awatar zostanie sprawdzony przez innego gracza, pojawi się prompt z opcją odwiedzenia doświadczenia, w którym został stworzony awatar.

Aby obsłużyć graczy dołączających do twojego doświadczenia za pomocą tego linku atrybucji, użyj Player:GetJoinData() i sparsuj zwróconą tabelę dla GameJoinContext.
GameJoinContext zawiera następujące wartości tabeli:
- JoinSource — Enum.JoinSource
- Player dołączający do twojego doświadczenia z tego linku atrybucji będzie miał Enum.JoinSource.CreatedItemAttribution, aby wskazać wejście z utworzonego przedmiotu.
- ItemType — opcjonalny Enum.AvatarItemType
- AssetId — opcjonalny string
- OutfitId — opcjonalny string
- AssetType — opcjonalny Enum.AssetType