您可以發布允許玩家在實時創建立、創作、自訂和購買虛擬人偶身體的體驗。購買時,這些自訂身體會直接儲存到玩家的 Roblox 道具欄中,允許玩家在其他體驗中裝備和穿戴自訂虛擬人偶。
在體驗中實現虛擬人偶創作的體驗擁有者可以從市場費用中受益,既是 創作者 ,又是 體驗擁有者 。如果在體驗中創建的資產被檢查,項目提供了鏈接到原始體驗,它被創建的原因。
您可以在 Roblox 的 虛擬人偶創作者 演示中測試經驗創作。
如何實現經驗內創建功作品
使用以下說明和代碼參考創建你的第一個體驗虛擬人偶創作項目。下列指令使用玩家可在發布前修改和自訂的基本身體 Model 。
開始之前,熟悉以追蹤中內容:
- 虛擬人偶創作代幣 — 實施虛擬人偶創作需要至少一個創作代幣。這些代幣需要 Robux 購買,並允許您設定體驗內購買的價格和其他銷售設定。
- API 類別
- AvatarCreationService — 處理創建虛擬人偶提示和驗證。
- EditableImage — 處理紋理的運行時創建和操作。
- EditableMesh — 處理網格幾何體的運行時操作。
- WrapDeformer — 允許虛擬人偶角色裝備3D服裝的透明外殼幾何操作運行時。
导入基础身身體
基本身體作為使用者可以自訂和編輯的初始基礎。您可以使用自己的 Model , 或使用 3D 匯入器 匯入自訂資產,並通過 虛擬人偶設定 設置。
基本身體必須遵守 Roblox 的虛擬人偶規格,並必須包含組件,例如 15 MeshPart 例構成 6 個身體部位:頭部、軀幹、左臂、左腿、右臂和右腿,以及其他 虛擬人偶零件 。
要查看正確配置的虛擬人偶身體的參考和樣本,請參閱虛擬人偶參考。
實現編輯 API
要開發一個系統,在體驗中的用戶可以編輯 MeshPart 個虛擬人偶上的實例以進行創作品,使用 EditableImage 來編輯紋理, EditableMesh 來編輯網格,並使用 WrapDeformer 來保持皮膚和 FACS 數據在網格編輯期間。
匯入基礎身體後,使用以下腳本設置您的EditableImages、EditableMeshes和WrapDeformers。
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))endlocal 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.SizenewMeshPart.CFrame = meshPart.CFramenewMeshPart.TextureContent = meshPart.TextureContent-- 將新的網格部件返回原始meshPart:ApplyMesh(newMeshPart)endlocal function setupMeshTexture(meshPart, textureIdToEditableImageMap)-- 如果此紋理ID已有可編輯的圖像,則不要創建新的圖像,而是使用它if textureIdToEditableImageMap[meshPart.TextureID] thenmeshPart.TextureContent =Content.fromObject(textureIdToEditableImageMap[meshPart.TextureID])returnend-- 創建一個新的可編輯圖像,並將其作為紋理內容應用local editableImage = AssetService:CreateEditableImageAsync(Content.fromUri(meshPart.TextureID))textureIdToEditableImageMap[meshPart.TextureID] = editableImagemeshPart.TextureContent = Content.fromObject(editableImage)endlocal function setupModel(model)-- 以紋理 ID重複可編輯圖像實例的地圖local textureIdToEditableImageMap = {}for _, descendant in model:GetDescendants() doif not descendant:IsA("MeshPart") thencontinueend-- 根據 WrapTarget 存在來配置網格零件-- 如果 WrapTarget 存在,請添加一個可編輯網格的 WrapDeformer 兒童-- 否則,直接將可編輯網格應用到網格零件local wrapTarget = descendant:FindFirstChildOfClass("WrapTarget")if wrapTarget thensetupBodyPart(descendant, wrapTarget)elsesetupRigidMesh(descendant)end-- 配置網格零件的可編輯圖像setupMeshTexture(descendant, textureIdToEditableImageMap)endend創建 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)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 = (game.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
使用 WrapDeformer 和 EditableMesh 創建工具來編輯你身體上的網格變形。
WrapDeformer 處理渲染的 MeshPart 幾何體的實時變形,同時保留底層皮膚和 FACS 數據。
EditableMesh 允許您修改 WrapDeformer 回應的籠子網格。
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
創建創建提示
設定基本身體和編輯 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 必須包含每個身體部位(Head、Torso、RightLeg、LeftLeg、RightArm、LeftArm)所需的新資產。可選擇地,還可以包括新的 Hair 飾品。
為了支持這一點, HumanoidDescription 應包含 6 BodyPartDescription 個孩子。每個 BodyPartDescription.Instance 屬性參考一個 Folder 包含所有構成身體部分的 MeshPart 實例。例如,LeftArm包含LeftHand、LeftUpperArm和LeftLowerArm``Class.MeshPart|MeshParts。屬性 BodyPartDescription.BodyPart 也應該設為相關的 Enum.BodyPart 。
每個 15 MeshPart 身體部位必須包括:
- An EditableImage .
- 一個 WrapDeformer 具有 EditableMesh 。
提供的 HumanoidDescription 不應包含任何已存在的資產ID來代表意圖創作品的身體部位或配件。反過來說,HumanoidDescription 可能包含 BodyTypeScale 、HeadScale 、HeightScale 、WidthScale 和 ProportionScale 的人形尺寸。請注意基礎身體所帶來的比例,以便它們與提供給 HumanoidDescription 的比例匹配。

包括配件
如果包含飾品,例如頭髮,HumanoidDescription 應包含一個子AccessoryDescription ,其中:
- AccessoryDescription.Instance 屬性參考 Accessory 實個體、實例。
- AccessoryDescription.AccessoryType設為相關的Enum.AccessoryType。
- 如果包含 Enum.AccessoryType.Hair 在你的創作中,那麼 MeshPart 應該包含一個 EditableImage 。然而,它不應包含 WrapDeformer 子但應包含在 EditableMesh 直接上的 MeshPart 集。
生成一個人物創作代幣
AvatarCreationService:PromptCreateAvatarAsync() 需要一個 創作者頭像創建代幣ID 參數。這個代幣是你宇宙中創作的關鍵,也是你可以使用它來從體驗設置虛擬人偶創作價格的東西。有關生成代幣的指示和額外細節,請參閱虛擬人偶創作代幣。
購買和生成代幣後,您可以在創作者中心檢查代幣,以查找ID,然後使用它來執行AvatarCreationService:PromptCreateAvatarAsync()。

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

若要處理玩家使用此認可綁定接加入您的體驗,請使用 Player:GetJoinData() 並分析返回的表格 GameJoinContext 。
GameJoinContext 包括以下表值:
- ItemType — 可選 Enum.AvatarItemType
- AssetId — 可選 string
- OutfitId — 可選 string
- AssetType — 可選 Enum.AssetType