Tworzenie awatara w dzieło

*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 awatara w czasie rzeczywistym.Po zakupie te niestandardowe ciała są zapisywane bezpośrednio w wyposażeniegracza w Roblox, co pozwala graczom wyposażać i nosić niestandardowe awatary w innych doświadczeniach.

Właściciele doświadczeń, którzy wdrażają tworzenie awatara w doświadczeniu, korzystają z opłat za rynek jako zarówno twórca przedmiotu awatara, jak i właściciel doświadczenia .Jeśli zasób utworzony w doświadczeniu zostanie sprawdzony, przedmiot zapewnia link do oryginalnego doświadczenia, w którym został utworzony.

Możesz przetestować tworzenie w doświadczeniu w demokracji Roblox'a Twórca awatara.

Jak wdrożyć tworzenie w dzieło

Użyj następujących instrukcji i odniesień kodu, aby stworzyć swój pierwszy projekt tworzenia awatara w doświadczeniu.Następujące instrukcje wykorzystują podstawowe ciało Model, które gracze mogą modyfikować i dostosowywać przed publikacją.

Zanim zaczniesz, zapoznaj się z obserwujekwestiami:

  • Modele awatarów — Następująca implementacja wymaga importowania podstawowego ciała, które spełnia specyfikacje części 15 Roblox.To Model służy jako podstawa dodatkowej personalizacji i modyfikacji użytkownika.
    • Baza ciała musi spełniać wytyczne dla ciała awatara Roblox'a Avatar body guidelines, w tym minimalną liczbę kontroli FACS dla rygowania twarzy.
  • Tokeny tworzenia awatara — doświadczenia implementujące tworzenie awatara wymagają co najmniej jednego tokenu tworzenia.Te tokeny wymagają Robux do zakupu i pozwalają ustawić ceny i inne ustawienia sprzedaży dla zakupów dokonywanych w doświadczeniu.
  • Klasy API
    • AvatarCreationService — obsługuje tworzenie awatara z prośbą o potwierdzenie i walidację.
    • EditableImage — Zarządza tworzeniem czasu wykonania i manipulacją teksturami.
    • EditableMesh — Rozwiązuje manipulację czasu wykonania geometrii siatki.
    • WrapDeformer — Rozwiązania do manipulacji czasu wykonania geometrii niewidzialnej zewnętrznej klatki, które pozwalają postaciom awatara wyposażyć 3D odzież.

Importuj podstawowe ciało

Podstawowe ciało działa jako początkowa podstawa, którą użytkownicy mogą dostosować i edytować.Możesz używać własnego Model lub importera niestandardowych zasobów za pomocą Importera 3D i skonfigurować za pomocą ustawienia awatara.

Bazy muszą przestrzegać specyfikacji awatara Roblox i muszą zawierać komponenty takie jak 15 instancje, które tworzą 6 części ciała: głowa, tułów, lewą rękę, lewą nogę, prawą rękę i prawą nogę, a także inne komponenty awatara.

Aby uzyskać odniesienia i przykłady prawidłowo skonfigurowanych ciał awatara, zobacz odniesienia awatara.

Wdroż edytowanie API

Aby opracować system, w którym użytkownicy mogą edytować instancje MeshPart na awatarze w swoim doświadczeniu do dzieło, użyj EditableImage do edycji tekstur, EditableMesh do edycji siatki i WrapDeformer do utrzymywania danych skóry i FACS podczas edycji siatki.

  1. Po zaimportowaniu twojego podstawowego ciałoużyj następującego skryptu, aby skonfigurować twoje EditableImages, EditableMeshes i WrapDeformers.


    local AssetService = game:GetService("AssetService")
    local function setupBodyPart(meshPart, wrapTarget)
    -- Stwórz i przymocuj deformator do MeshPart
    local wrapDeformer = Instance.new("WrapDeformer")
    wrapDeformer.Parent = meshPart
    -- Stwórz edytowalną siatkę dla siatki klatki celu zawijania
    local cageEditableMesh: EditableMesh =
    AssetService:CreateEditableMeshAsync(Content.fromUri(wrapTarget.CageMeshId), {
    FixedSize = true,
    })
    -- Nadaj siatce klatki do WrapDeformer
    wrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh))
    end
    local function setupRigidMesh(meshPart)
    -- Stwórz edytowalną siatkę z oryginalnej części siatki
    local editableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri(meshPart.MeshId), {
    FixedSize = true,
    })
    -- Wygeneruj nową część siatki z edytowanej siatki
    local newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh))
    -- Kopiuj rozmiar, pozycję i teksturę z oryginalnej części siatki
    newMeshPart.Size = meshPart.Size
    newMeshPart.CFrame = meshPart.CFrame
    newMeshPart.TextureContent = meshPart.TextureContent
    -- Zastosuj nową część siatki z powrotem do oryginału
    meshPart:ApplyMesh(newMeshPart)
    end
    local function setupMeshTexture(meshPart, textureIdToEditableImageMap)
    -- Jeśli Edytowane obraz istnieje już dla tego ID tekstury, użyj go zamiast tworzyć nowy
    if textureIdToEditableImageMap[meshPart.TextureID] then
    meshPart.TextureContent =
    Content.fromObject(textureIdToEditableImageMap[meshPart.TextureID])
    return
    end
    -- Stwórz nowy edytowalny obraz 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)
    -- Mapa dla ponownego wykorzystania instancji EditableImage przez ID tekstury
    local textureIdToEditableImageMap = {}
    for _, descendant in model:GetDescendants() do
    if not descendant:IsA("MeshPart") then
    continue
    end
    -- Konfiguruj MeshPart w oparciu o obecność WrapTarget
    -- Jeśli WrapTarget jest obecny, dodaj dziecko WrapDeformer z edytowalną siatką
    -- W przeciwnym razie zastosuj Edytowalną Sieć bezpośrednio do MeshPart
    local wrapTarget = descendant:FindFirstChildOfClass("WrapTarget")
    if wrapTarget then
    setupBodyPart(descendant, wrapTarget)
    else
    setupRigidMesh(descendant)
    end
    -- Konfiguruj Edytowalny Obraz dla części siatki
    setupMeshTexture(descendant, textureIdToEditableImageMap)
    end
    end
  2. Twórz narzędzia EditableImage, które pozwalają graczom na odmalowanie, narysowanie lub dodanie naklejek do twojego podstawowego ciało.Możesz wykorzystać API w takich przypadkach jak DrawImage(), DrawRectangle(), WritePixelsBuffer().

    • Dla zaawansowanych transformacji pozwala określić pozycję, rotację i skalę podczas rysowania jednego Edytowanego obrazu na drugim.Podobnie, DrawImageProjected() działa podobnie do DrawImage() ale projektuje poprawnie rysowane obrazy, 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 = (game.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 edycji deformacji siatki na swoim ciało.

    1. WrapDeformer radzi sobie z żywymi deformacjami renderowanej MeshPart geometrii, zachowując podstawowe dane skinningowe i FACS.

    2. EditableMesh pozwala modyfikować siatkę klatki, na którą odpowiada WrapDeformer .

      1. Użyj WrapDeformer:SetCageMeshContent() aby zastosować instancję EditableMesh reprezentującą odpowiednią siatkę klatki do WrapDeformer.
      2. Użyj EditableMesh, takich jak SetPosition(), aby zniekształcić wierzety 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

Twórz powiadomienie o tworzeniu

Po skonfigurowaniu podstawowego ciała i edytowaniu interfejsów programowania utwórz powiadomienie dla użytkowników, aby stworzyli i kupili z doświadczenia za pomocą AvatarCreationService:PromptCreateAvatarAsync().


export type BodyPartInfo = {
bodyPart: Enum.BodyPart,
instance: Instance --Katalog z utworzonymi częściami siatki
}
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("Successfully uploaded with BundleId: ", bundleIdOrErrorMessage)
print("Successfully uploaded with OutfitId: ", outfitId)
else
print("Unsuccessfully uploaded with error message:", bundleIdOrErrorMessage)
end
else
print("Avatar creation unsuccessful")
end
end

AvatarCreationService:PromptCreateAvatarAsync() bierze parametr HumanoidDescription do reprezentowania awatara przeznaczonego do zakupu lub dzieło.Aby dziełoawatar, postać HumanoidDescription musi zawierać nowe zasoby, które należy stworzyć dla każdej z 6 części ciała (Head , Torso , RightLeg , LeftLeg , RightArm , LeftArm ).Opcjonalnie może również zawierać nowy Hair akcesorium.

Aby to wspierać, HumanoidDescription powinno zawierać 6 BodyPartDescription dzieci.Każda właściwość BodyPartDescription.Instance odnosi się do Folder, która zawiera wszystkie instancje MeshPart, które tworzą część ciała.Na przykład, katalog LeftArm zawiera LeftHand , LeftUpperArm , i LeftLowerArm``Class.MeshPart|MeshParts .Właściwość BodyPartDescription.BodyPart powinna również zostać ustawiona na odpowiednią Enum.BodyPart.

Każda z 15 części ciała MeshPart musi zawierać:

Dostarczone HumanoidDescription nie powinno zawierać żadnych wcześniej istniejących identyfikatorów zasobów, aby reprezentować części ciała lub akcesoria w zamierzonej tworzonej dzieło.Z drugiej strony, HumanoidDescription może zawierać ludzkie skale BodyTypeScale , HeadScale , HeightScale , WidthScale i ProportionScale .Bądź ostrożny z skalami, z którymi zaimportowano podstawowe ciało, aby pasowały do skal dostarczonych do HumanoidDescription.

Załóż akcesoria

Jeśli włącza się akcesorium, takie jak włosy, HumanoidDescription powinno zawierać dziecko AccessoryDescription, gdzie:

Generuj token tworzenia awatara

AvatarCreationService:PromptCreateAvatarAsync() bierze parametr ID tworzenia awatara .Ten token jest kluczem do tworów z twojego wszechświata i jest tym, co możesz użyć, aby ustawić cenę tworzenia awatara z twojego doświadczenia.Aby uzyskać instrukcje i dodatkowe szczegóły dotyczące generowania tokenów, zobacz Tokeny tworzenia awatara.

Po zakupie i generowaniu swojego tokenu możesz sprawdzić token w Centrum twórców, aby znaleźć ID, które możesz następnie użyć do API AvatarCreationService:PromptCreateAvatarAsync().

Odpowiadaj na dołączanie graczy przez przypisywanie

Paczki awatarów utworzone w doświadczeniu zawierają link do przypisania do oryginalnego doświadczenia, w którym powstał awatar.Jeśli awatar jest sprawdzany przez innego gracza, wyświetla się monit o wybór opcji odwiedzenia doświadczenia, w którym powstał awatar.

Aby obsłużyć graczy dołączających do twojego doświadczenia za pomocą tego łączyćprzypisania, użyj Player:GetJoinData() i przetwarzaj zwróconą tabelę dla GameJoinContext.

GameJoinContext zawiera następujące wartości tabeli: