體驗內的人物創作品

*此內容是使用 AI(Beta 測試版)翻譯,可能含有錯誤。若要以英文檢視此頁面,請按一下這裡

您可以發布允許玩家在實時創建立、創作、自訂和購買虛擬人偶身體的體驗。購買時,這些自訂身體會直接儲存到玩家的 Roblox 道具欄中,允許玩家在其他體驗中裝備和穿戴自訂虛擬人偶。

在體驗中實現虛擬人偶創作的體驗擁有者可以從市場費用中受益,既是 創作者 ,又是 體驗擁有者 。如果在體驗中創建的資產被檢查,項目提供了鏈接到原始體驗,它被創建的原因。

您可以在 Roblox 的 虛擬人偶創作者 演示中測試經驗創作。

如何實現經驗內創建功作品

使用以下說明和代碼參考創建你的第一個體驗虛擬人偶創作項目。下列指令使用玩家可在發布前修改和自訂的基本身體 Model

開始之前,熟悉以追蹤中內容:

  • 虛擬人偶模型 — 以下實現需要匯入符合 Roblox 15 部分規格的基本身體。這 Model 可用作額外用戶自訂和修改的基礎。
    • 基本身體必須符合 Roblox 的 人物身體指南,包括面部骨架化的最低數量的 FACS 控制。
  • 虛擬人偶創作代幣 — 實施虛擬人偶創作需要至少一個創作代幣。這些代幣需要 Robux 購買,並允許您設定體驗內購買的價格和其他銷售設定。
  • API 類別

导入基础身身體

基本身體作為使用者可以自訂和編輯的初始基礎。您可以使用自己的 Model , 或使用 3D 匯入器 匯入自訂資產,並通過 虛擬人偶設定 設置。

基本身體必須遵守 Roblox 的虛擬人偶規格,並必須包含組件,例如 15 MeshPart 例構成 6 個身體部位:頭部、軀幹、左臂、左腿、右臂和右腿,以及其他 虛擬人偶零件

要查看正確配置的虛擬人偶身體的參考和樣本,請參閱虛擬人偶參考

實現編輯 API

要開發一個系統,在體驗中的用戶可以編輯 MeshPart 個虛擬人偶上的實例以進行創作品,使用 EditableImage 來編輯紋理, EditableMesh 來編輯網格,並使用 WrapDeformer 來保持皮膚和 FACS 數據在網格編輯期間。

  1. 匯入基礎身體後,使用以下腳本設置您的EditableImagesEditableMeshesWrapDeformers


    local AssetService = game:GetService("AssetService")
    local function setupBodyPart(meshPart, wrapTarget)
    -- 創建並附加包裝變形器到網格零件
    local wrapDeformer = Instance.new("WrapDeformer")
    wrapDeformer.Parent = meshPart
    -- 為包裝目標的網格網創建可編輯的網格網
    local cageEditableMesh: EditableMesh =
    AssetService:CreateEditableMeshAsync(Content.fromUri(wrapTarget.CageMeshId), {
    FixedSize = true,
    })
    -- 將籠網交付給包裝變形器
    wrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh))
    end
    local function setupRigidMesh(meshPart)
    -- 從原始的MeshPart中創建可編輯的網格
    local editableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri(meshPart.MeshId), {
    FixedSize = true,
    })
    -- 從可編輯的網格生成新的網格零件
    local newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh))
    -- 從原始 MeshPart 複製尺寸、位置和紋理
    newMeshPart.Size = meshPart.Size
    newMeshPart.CFrame = meshPart.CFrame
    newMeshPart.TextureContent = meshPart.TextureContent
    -- 將新的網格部件返回原始
    meshPart:ApplyMesh(newMeshPart)
    end
    local function setupMeshTexture(meshPart, textureIdToEditableImageMap)
    -- 如果此紋理ID已有可編輯的圖像,則不要創建新的圖像,而是使用它
    if textureIdToEditableImageMap[meshPart.TextureID] then
    meshPart.TextureContent =
    Content.fromObject(textureIdToEditableImageMap[meshPart.TextureID])
    return
    end
    -- 創建一個新的可編輯圖像,並將其作為紋理內容應用
    local editableImage = AssetService:CreateEditableImageAsync(Content.fromUri(meshPart.TextureID))
    textureIdToEditableImageMap[meshPart.TextureID] = editableImage
    meshPart.TextureContent = Content.fromObject(editableImage)
    end
    local function setupModel(model)
    -- 以紋理 ID重複可編輯圖像實例的地圖
    local textureIdToEditableImageMap = {}
    for _, descendant in model:GetDescendants() do
    if not descendant:IsA("MeshPart") then
    continue
    end
    -- 根據 WrapTarget 存在來配置網格零件
    -- 如果 WrapTarget 存在,請添加一個可編輯網格的 WrapDeformer 兒童
    -- 否則,直接將可編輯網格應用到網格零件
    local wrapTarget = descendant:FindFirstChildOfClass("WrapTarget")
    if wrapTarget then
    setupBodyPart(descendant, wrapTarget)
    else
    setupRigidMesh(descendant)
    end
    -- 配置網格零件的可編輯圖像
    setupMeshTexture(descendant, textureIdToEditableImageMap)
    end
    end
  2. 創建 EditableImage 允許玩家重新上色、繪畫或添加貼紙到你基礎身體的工具。您可以利用 API 在像 DrawImage() , DrawRectangle() , WritePixelsBuffer() 中一樣。

    • 對於高級變形,DrawImageTransformed()允許您在繪製一個可編輯圖像到另一個時指定位置、旋轉和縮放。相同地, DrawImageProjected() 工作非常像 DrawImage() ,但如果 EditableImage 實例使用 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. 使用 WrapDeformerEditableMesh 創建工具來編輯你身體上的網格變形。

    1. WrapDeformer 處理渲染的 MeshPart 幾何體的實時變形,同時保留底層皮膚和 FACS 數據。

    2. EditableMesh 允許您修改 WrapDeformer 回應的籠子網格。

      1. 使用 WrapDeformer:SetCageMeshContent() 將代表相關籠子網格的 EditableMesh 實例應用到 WrapDeformer
      2. 使用 EditableMesh , 例如 SetPosition() , 來扭曲邊緣並編輯 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

創建創建提示

設定基本身體和編輯 API 之後,創建一個提示,讓使用者使用 AvatarCreationService:PromptCreateAvatarAsync() 從體驗中創建和購買。


export type BodyPartInfo = {
bodyPart: Enum.BodyPart,
instance: Instance --創建網格零件的文件夾
}
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() 使用 HumanoidDescription 參數來代表用於購買或作品的虛擬人偶。對於虛擬人偶的創作品,角色的 HumanoidDescription 必須包含每個身體部位(HeadTorsoRightLegLeftLegRightArmLeftArm)所需的新資產。可選擇地,還可以包括新的 Hair 飾品。

為了支持這一點, HumanoidDescription 應包含 6 BodyPartDescription 個孩子。每個 BodyPartDescription.Instance 屬性參考一個 Folder 包含所有構成身體部分的 MeshPart 實例。例如,LeftArm包含LeftHandLeftUpperArmLeftLowerArm``Class.MeshPart|MeshParts。屬性 BodyPartDescription.BodyPart 也應該設為相關的 Enum.BodyPart

每個 15 MeshPart 身體部位必須包括:

提供的 HumanoidDescription 不應包含任何已存在的資產ID來代表意圖創作品的身體部位或配件。反過來說,HumanoidDescription 可能包含 BodyTypeScaleHeadScaleHeightScaleWidthScaleProportionScale 的人形尺寸。請注意基礎身體所帶來的比例,以便它們與提供給 HumanoidDescription 的比例匹配。

包括配件

如果包含飾品,例如頭髮,HumanoidDescription 應包含一個子AccessoryDescription ,其中:

生成一個人物創作代幣

AvatarCreationService:PromptCreateAvatarAsync() 需要一個 創作者頭像創建代幣ID 參數。這個代幣是你宇宙中創作的關鍵,也是你可以使用它來從體驗設置虛擬人偶創作價格的東西。有關生成代幣的指示和額外細節,請參閱虛擬人偶創作代幣

購買和生成代幣後,您可以在創作者中心檢查代幣,以查找ID,然後使用它來執行AvatarCreationService:PromptCreateAvatarAsync()

透過歸屬回應玩家加入

體驗內創建的虛擬人偶包括一個鏈接到原始體驗的虛擬人偶的歸屬。如果頭像被其他玩家檢查,會顯示提示,提供訪問頭像創建的體驗的選擇。

若要處理玩家使用此認可綁定接加入您的體驗,請使用 Player:GetJoinData() 並分析返回的表格 GameJoinContext

GameJoinContext 包括以下表值:

  • JoinSourceEnum.JoinSource
    • 從此歸因鏈接加入您的體驗會有 指示從創建的道具目中進入。
  • ItemType — 可選 Enum.AvatarItemType
  • AssetId — 可選 string
  • OutfitId — 可選 string
  • AssetType — 可選 Enum.AssetType