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 zapisywane są bezpośrednio w inwentarzu Roblox gracza, co pozwala graczom na wyposażenie i noszenie niestandardowych awatarów w innych doświadczeniach.
Właściciele doświadczeń, którzy wdrażają tworzenie awatarów w doświadczeniu, korzystają z prowizji rynkowych jako zarówno twórca przedmiotu awatara, jak i właściciel doświadczenia. Jeśli aktyw wyprodukowany w doświadczeniu zostanie sprawdzony, przedmiot dostarcza link do oryginalnego doświadczenia, w którym został stworzony.
Możesz przetestować tworzenie awatarów w doświadczeniu na demonstracji Kreatora Awatarów w Robloxie.
Jak wdrożyć tworzenie w doświadczeniu
Użyj poniższych instrukcji i odniesień 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 opublikowaniem.
Przed rozpoczęciem, zaznajom się z następującymi:
- Modele awatarów — Poniższa implementacja wymaga zaimportowania podstawowego ciała, które spełnia specyfikację 15 części Roblox. To Model służy jako podstawa do dodatkowej personalizacji i modyfikacji przez użytkowników.
- Podstawowe ciało musi spełniać wytyczne dotyczące ciała awatarów Roblox, w tym minimalną liczbę kontrolerów FACS do rigowania twarzy.
- Tokenu do tworzenia awatarów — Doświadczenia realizujące tworzenie awatarów wymagają co najmniej jednego tokenu do tworzenia. Te tokeny wymagają Robux do zakupu i pozwalają na ustalanie cen oraz innych ustawień sprzedaży dla zakupów dokonanych w doświadczeniu.
- Klasy API
- AvatarCreationService — Obsługuje żądania tworzenia awatarów i walidację.
- EditableImage — Obsługuje tworzenie i manipulację teksturami w czasie rzeczywistym.
- EditableMesh — Obsługuje manipulację geometrią siatki w czasie rzeczywistym.
- WrapDeformer — Obsługuje manipulację niewidzialną geometrią zewnętrznej klatki, która pozwala postaciom awatarów używać 3D odzieży.
Importuj podstawowe ciało
Podstawowe ciało działa jako początkowa podstawa, którą użytkownicy mogą dostosowywać i edytować. Możesz użyć własnego Model lub zaimportować niestandardowy zasób za pomocą Importera i skonfigurować przez Ustawienia awatara.
Podstawowe ciała muszą przestrzegać specyfikacji awatarów Roblox i muszą zawierać komponenty takie jak 15 instancji MeshPart, które składają się na 6 części ciała: głowa, tors, lewy ramie, lewa noga, prawa ramie i prawa noga, a także inne komponenty awatarów.
Aby uzyskać odniesienia i próbki poprawnie skonfigurowanych ciał awatarów, zobacz Odniesienia do awatarów.
Wdrożenie edytowalnych API
Aby opracować system, w którym użytkownicy mogą edytować instancje MeshPart na awatarze w Twoim doświadczeniu, aby tworzyć niestandardowe modele, użyj EditableImage do edytowania tekstur, EditableMesh do edytowania siatek i WrapDeformer do utrzymywania danych o przypisaniu i FACS podczas edycji siatek.
Po zaimportowaniu podstawowego ciała, użyj następującego skryptu, aby skonfigurować swoje EditableImages, EditableMeshes i WrapDeformers.
local AssetService = game:GetService("AssetService")local function setupBodyPart(meshPart, wrapTarget)-- Tworzenie i dołączenie WrapDeformer do MeshPartlocal wrapDeformer = Instance.new("WrapDeformer")wrapDeformer.Parent = meshPart-- Tworzenie edytowalnej siatki dla siatki klatki celulocal cageEditableMesh: EditableMesh =AssetService:CreateEditableMeshAsync(Content.fromUri(wrapTarget.CageMeshId), {FixedSize = true,})-- Przypisanie siatki klatki do WrapDeformerwrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh))endlocal function setupRigidMesh(meshPart)-- Tworzenie edytowalnej siatki z oryginalnego MeshPartlocal editableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri(meshPart.MeshId), {FixedSize = true,})-- Generowanie nowego MeshPart z edytowalnej siatkilocal newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh))-- Kopiowanie rozmiaru, pozycji i tekstury z oryginalnego MeshPartnewMeshPart.Size = meshPart.SizenewMeshPart.CFrame = meshPart.CFramenewMeshPart.TextureContent = meshPart.TextureContent-- Zastosowanie nowego MeshPart z powrotem do oryginalnegomeshPart:ApplyMesh(newMeshPart)endlocal function setupMeshTexture(meshPart, textureIdToEditableImageMap)-- Jeśli EditableImage już istnieje dla tego TextureID, użyj go zamiast tworzyć nowyif textureIdToEditableImageMap[meshPart.TextureID] thenmeshPart.TextureContent =Content.fromObject(textureIdToEditableImageMap[meshPart.TextureID])returnend-- Tworzenie nowego EditableImage i zastosowanie go jako zawartości 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-- Konfigurowanie 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-- Konfigurowanie EditableImage dla MeshPartsetupMeshTexture(descendant, textureIdToEditableImageMap)endendStwórz narzędzia EditableImage, które pozwalają graczom na zmienianie kolorów, rysowanie lub dodawanie naklejek do podstawowego ciała. Możesz wykorzystać API takie jak DrawImage(), DrawRectangle(), WritePixelsBuffer().
Dla zaawansowanych transformacji, DrawImageTransformed() pozwala na określenie pozycji, rotacji i skali podczas rysowania jednego EditableImage na innym. Podobnie, DrawImageProjected() działa prawie jak DrawImage(), ale prawidłowo projecuje rysowany 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, utwórz narzędzia do edytowania deformacji siatek w swoim ciele.
WrapDeformer obsługuje żywe deformacje geometrii renderowanej MeshPart, zachowując przy tym dane o przypisaniu 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, na przykład SetPosition(), aby deformować 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
Stwórz zachętę do tworzenia
Po skonfigurowaniu podstawowego ciała i edytowalnych API, stwórz zachętę dla użytkowników do tworzenia i zakupu z doświadczenia za pomocą 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("Niepowodzenie przesłania z komunikatem o błędzie:", bundleIdOrErrorMessage)
end
else
print("Tworzenie awatara nieudane")
end
end
AvatarCreationService:PromptCreateAvatarAsync() przyjmuje parametr HumanoidDescription, aby reprezentować awatar przeznaczony do zakupu lub tworzenia. W przypadku tworzenia awatara opis HumanoidDescription postaci musi zawierać nowe aktywa, które mają być utworzone dla każdej z 6 części ciała (Głowa, Tors, PrawaNoga, LewaNoga, PrawaRęka, LewaRęka). Opcjonalnie może także zawierać nowy akcesorium Włosy.
Aby to wspierać, HumanoidDescription powinien zawierać 6 dzieci typu BodyPartDescription. Każda właściwość BodyPartDescription.Instance odnosi się do Folder, która zawiera wszystkie instancje MeshPart składające się na część ciała. Na przykład folder LewaRęka zawiera LewaDłoń, LewaGórnaRęka i LewaDolaRęka MeshParts. Właściwość BodyPartDescription.BodyPart powinna być również ustawiona na odpowiedni Enum.BodyPart.
Każda z 15 części ciała MeshPart musi zawierać:
Dostarczony opis HumanoidDescription nie powinien zawierać żadnych ID aktywów, które już istnieją, aby reprezentować części ciała lub akcesoria w zamierzonym utworze. Z drugiej strony opis HumanoidDescription może zawierać skale humanoidalne BodyTypeScale, HeadScale, HeightScale, WidthScale, oraz ProportionScale. Uważaj na skale, z jakimi zaimportowane jest podstawowe ciało, aby były zgodne z tymi, które zostały dostarczone do HumanoidDescription.

Dołącz akcesoria
Jeśli dołączasz akcesorium, takie jak włosy, opis HumanoidDescription powinien zawierać dziecko AccessoryDescription, w którym:
- Właściwość AccessoryDescription.Instance odnosi się do instancji Accessory.
- Właściwość AccessoryDescription.AccessoryType jest ustawiona na odpowiedni Enum.AccessoryType.
- W przypadku dołączania Enum.AccessoryType.Hair do swoich tworów, MeshPart powinno zawierać EditableImage. Jednak nie powinno zawierać dziecka WrapDeformer, ale powinno mieć ustawiony EditableMesh bezpośrednio na MeshPart.
Generuj token do tworzenia awatarów
AvatarCreationService:PromptCreateAvatarAsync() przyjmuje parametr ID tokenu do tworzenia awatarów. Ten token jest kluczem do tworzenia z Twojego wszechświata i to jest to, czego możesz użyć do ustawienia ceny tworzenia awatarów z Twojego doświadczenia. Aby uzyskać instrukcje i dodatkowe szczegóły dotyczące generowania tokenów, zobacz Tokeny do tworzenia awatarów.
Po zakupie i wygenerowaniu tokenu, możesz sprawdzić token w Centrum Twórcy, aby znaleźć ID, które możesz następnie wykorzystać do API AvatarCreationService:PromptCreateAvatarAsync().

Odpowiedz na dołączanie graczy przez atrybucję
Zestawy awatarów tworzone w doświadczeniu zawierają link atrybucji do oryginalnego doświadczenia, w którym awatar został stworzony. Jeśli awatar zostanie sprawdzony przez innego gracza, zostanie wyświetlony monit z opcją odwiedzenia doświadczenia, w którym awatar został stworzony.

Aby obsłużyć graczy dołączających do Twojego doświadczenia za pomocą tego linku atrybucji, użyj Player:GetJoinData() i przeanalizuj zwróconą tabelę pod kątem 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 stworzonego przedmiotu.
- ItemType — opcjonalny Enum.AvatarItemType
- AssetId — opcjonalny string
- OutfitId — opcjonalny string
- AssetType — opcjonalny Enum.AssetType