Tworzenie awatarów w doświadczeniach

*Ta zawartość została przetłumaczona przy użyciu narzędzi AI (w wersji beta) i może zawierać błędy. Aby wyświetlić tę stronę w języku angielskim, kliknij tutaj.

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.
  • 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.

  1. 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 MeshPart
    local wrapDeformer = Instance.new("WrapDeformer")
    wrapDeformer.Parent = meshPart
    -- Utwórz edytowalną siatkę dla siatki klatki docelowej
    local cageEditableMesh: EditableMesh =
    AssetService:CreateEditableMeshAsync(Content.fromUri(wrapTarget.CageMeshId), {
    FixedSize = true,
    })
    -- Przypisz siatkę klatki do WrapDeformer
    wrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh))
    end
    local function setupRigidMesh(meshPart)
    -- Utwórz edytowalną siatkę z oryginalnego MeshPart
    local editableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri(meshPart.MeshId), {
    FixedSize = true,
    })
    -- Generuj nowy MeshPart z edytowalnej siatki
    local newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh))
    -- Skopiuj rozmiar, pozycję i teksturę z oryginalnego MeshPart
    newMeshPart.Size = meshPart.Size
    newMeshPart.CFrame = meshPart.CFrame
    newMeshPart.TextureContent = meshPart.TextureContent
    -- Zastosuj nowy MeshPart do oryginalnego
    meshPart:ApplyMesh(newMeshPart)
    end
    local function setupMeshTexture(meshPart, textureIdToEditableImageMap)
    -- Jeśli EditableImage już istnieje dla tego TextureID, użyj go ponownie zamiast tworzyć nowego
    if textureIdToEditableImageMap[meshPart.TextureID] then
    meshPart.TextureContent =
    Content.fromObject(textureIdToEditableImageMap[meshPart.TextureID])
    return
    end
    -- Utwórz nowy EditableImage i zastosuj go jako zawartość tekstury
    local editableImage = AssetService:CreateEditableImageAsync(Content.fromUri(meshPart.TextureID))
    textureIdToEditableImageMap[meshPart.TextureID] = editableImage
    meshPart.TextureContent = Content.fromObject(editableImage)
    end
    local function setupModel(model)
    -- Mapowanie do ponownego użycia instancji EditableImage według ID tekstury
    local textureIdToEditableImageMap = {}
    for _, descendant in model:GetDescendants() do
    if not descendant:IsA("MeshPart") then
    continue
    end
    -- 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 MeshPart
    local wrapTarget = descendant:FindFirstChildOfClass("WrapTarget")
    if wrapTarget then
    setupBodyPart(descendant, wrapTarget)
    else
    setupRigidMesh(descendant)
    end
    -- Skonfiguruj EditableImage dla MeshPart
    setupMeshTexture(descendant, textureIdToEditableImageMap)
    end
    end
  2. Stwó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
      )
      end
      local 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)
      end
      local 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).Unit
      local 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
  3. Używając WrapDeformer i EditableMesh, stwórz narzędzia do edytowania deformacji siatki na twoim ciele.

    1. WrapDeformer obsługuje na żywo deformacje renderowanej geometrii MeshPart, zachowując dane o skórze i FACS.

    2. EditableMesh pozwala na modyfikację siatki klatki, na którą reaguje WrapDeformer.

      1. Użyj WrapDeformer:SetCageMeshContent(), aby zastosować instancję EditableMesh, która reprezentuje odpowiednią siatkę klatki do WrapDeformer.
      2. 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.CageMeshId
      local wrapDeformer = Instance.new("WrapDeformer")
      wrapDeformer.Parent = meshPart
      local cageEditableMesh = AssetService:CreateEditableMeshAsync(cageMeshId)
      local verticesWithinSphere =
      cageEditableMesh:FindVerticesWithinSphere(controlPointCenter, controlPointRadius)
      for _, vertexId in verticesWithinSphere do
      local vertexPosition = cageEditableMesh:GetPosition(vertexId)
      cageEditableMesh:SetPosition(vertexId, vertexPosition + controlPointDeformation)
      end
      wrapDeformer: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
end

AvatarCreationService: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:

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:

©2026 Roblox Corporation. Nazwa Roblox, logo Roblox oraz hasło „Powering Imagination” należą do naszych zarejestrowanych i niezarejestrowanych znaków towarowych na terenie Stanów Zjednoczonych oraz w innych krajach.