您可以發布一個允許玩家實時創建、自定義和購買角色的體驗。當角色被購買時,這些自定義角色會直接保存在玩家的 Roblox 庫中,允許玩家在其他體驗中裝備和穿戴這些自定義角色。
實施體驗中角色創建的體驗擁有者可以同時作為角色項目的 創作者 和 體驗擁有者 受益於 市場佣金。如果在體驗中創建的資產被檢查,該項目會提供鏈接到創建該資產的原始體驗。
您可以在 Roblox 的 角色創建器 演示中測試此功能。
如何實施體驗中的創建
使用以下指導和代碼參考來創建您的第一個體驗中角色創建項目。以下指令使用一個基礎身體 Model,玩家可以對其進行修改和自定義,然後再發布。
在開始之前,請熟悉以下內容:
- 角色創建令牌 — 實現角色創建的體驗至少需要一個創建令牌。這些令牌需要用 Robux 購買,並允許您設置在體驗中進行購買的價格和其他銷售設置。
- API 類
- AvatarCreationService — 處理角色創建的提示和驗證。
- EditableImage — 處理可編輯文本的運行時創建和操作。
- EditableMesh — 處理網格幾何的運行時操作。
- WrapDeformer — 處理可使角色裝備 3D 服裝 的可見外部外殼幾何的運行時操作。
導入基礎身體
基礎身體作為用戶可以自定義和編輯的初始基礎。您可以使用自己的 Model,或使用 導入工具 導入自定義資產並通過 角色設置 進行設置。
基礎身體必須遵守 Roblox 的角色規範,並且必須包括 15 個 MeshPart 實例,這些實例組成 6 個身體部位:頭部、軀幹、左臂、左腿、右臂和右腿,以及其他 角色部件。
有關正確配置角色身體的參考和示例,請參閱 角色參考。
實施編輯 API
要開發一個用戶可以在您的體驗中的角色上編輯 MeshPart 實例的系統,請使用 EditableImage 進行紋理編輯,使用 EditableMesh 進行網格編輯,以及使用 WrapDeformer 在網格編輯過程中保持蒙皮和 FACS 數據。
載入您的基礎身體後,使用以下腳本設置您的 EditableImages、EditableMeshes 和 WrapDeformers。
local AssetService = game:GetService("AssetService")local function setupBodyPart(meshPart, wrapTarget)-- 創建並將 WrapDeformer 附加到 MeshPartlocal wrapDeformer = Instance.new("WrapDeformer")wrapDeformer.Parent = meshPart-- 為包圍目標的籠子網格創建可編輯網格local cageEditableMesh: EditableMesh =AssetService:CreateEditableMeshAsync(Content.fromUri(wrapTarget.CageMeshId), {FixedSize = true,})-- 將籠子網格分配給 WrapDeformerwrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh))endlocal function setupRigidMesh(meshPart)-- 從原始 MeshPart 創建可編輯網格local editableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri(meshPart.MeshId), {FixedSize = true,})-- 根據可編輯網格生成新的 MeshPartlocal newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh))-- 從原始 MeshPart 複製大小、位置和紋理newMeshPart.Size = meshPart.SizenewMeshPart.CFrame = meshPart.CFramenewMeshPart.TextureContent = meshPart.TextureContent-- 將新的 MeshPart 應用回原始meshPart:ApplyMesh(newMeshPart)endlocal function setupMeshTexture(meshPart, textureIdToEditableImageMap)-- 如果該 TextureID 已存在 EditableImage,則重用它,而不是創建新的if textureIdToEditableImageMap[meshPart.TextureID] thenmeshPart.TextureContent =Content.fromObject(textureIdToEditableImageMap[meshPart.TextureID])returnend-- 創建新的 EditableImage 並將其應用為紋理內容local editableImage = AssetService:CreateEditableImageAsync(Content.fromUri(meshPart.TextureID))textureIdToEditableImageMap[meshPart.TextureID] = editableImagemeshPart.TextureContent = Content.fromObject(editableImage)endlocal function setupModel(model)-- 按紋理 ID 重用 EditableImage 實例的映射local textureIdToEditableImageMap = {}for _, descendant in model:GetDescendants() doif not descendant:IsA("MeshPart") thencontinueend-- 根據 WrapTarget 存在情況配置 MeshPart-- 如果存在 WrapTarget,則添加 WrapDeformer 子項,並帶有 EditableMesh-- 否則,直接將 EditableMesh 應用於 MeshPartlocal wrapTarget = descendant:FindFirstChildOfClass("WrapTarget")if wrapTarget thensetupBodyPart(descendant, wrapTarget)elsesetupRigidMesh(descendant)end-- 配置 MeshPart 的 EditableImagesetupMeshTexture(descendant, textureIdToEditableImageMap)endend創建 EditableImage 工具,允許玩家為您的基礎身體重新上色、繪畫或添加貼紙。您可以利用 DrawImage()、DrawRectangle()、WritePixelsBuffer() 等 API。
對於高級變換,DrawImageTransformed() 允許您在繪畫一個 EditableImage 時指定位置、旋轉和縮放。同樣,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 = (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 --帶有創建的 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("成功上傳,BundleId: ", bundleIdOrErrorMessage)
print("成功上傳,OutfitId: ", outfitId)
else
print("上傳失敗,錯誤信息:", bundleIdOrErrorMessage)
end
else
print("角色創建不成功")
end
endAvatarCreationService:PromptCreateAvatarAsync() 需要一個 HumanoidDescription 參數來表示打算購買或創建的角色。要進行角色創建,角色的 HumanoidDescription 必須包括每個 6 個身體部位的新資產 (Head, Torso, RightLeg, LeftLeg, RightArm, LeftArm)。選擇性地,它還可以包括一個新的 Hair 配件。
為了支持這一點,HumanoidDescription 應該包括 6 個 BodyPartDescription 子項。每個 BodyPartDescription.Instance 屬性引用包含所有構成身體部位的 MeshPart 實例的 Folder。例如,LeftArm 文件夾包含 LeftHand、LeftUpperArm 和 LeftLowerArm 的 MeshParts。 BodyPartDescription.BodyPart 屬性也應設置為相關的 Enum.BodyPart。
每個 15 個 MeshPart 的身體部位必須包括:
- 一個 EditableImage。
- 一個帶有 EditableMesh 的 WrapDeformer。
提供的 HumanoidDescription 不應包含任何預先存在的資產 ID 來表示預期創建的身體部位或配件。另一方面,HumanoidDescription 可能包括 BodyTypeScale、HeadScale、HeightScale、WidthScale和 ProportionScale 的人類比例。請注意基礎身體的比例應與提供給 HumanoidDescription 的比例相匹配。

包含配件
如果包含配件,例如頭髮,則 HumanoidDescription 應該包括一個子 AccessoryDescription,其中:
- AccessoryDescription.Instance 屬性引用 Accessory 實例。
- AccessoryDescription.AccessoryType 屬性設定為相關的 Enum.AccessoryType。
- 在創建中包含 Enum.AccessoryType.Hair 的情況下,MeshPart 應包括一個 EditableImage。然而,它不應該包括一個 WrapDeformer 子項,而應該在 MeshPart 上直接包括一個 EditableMesh。
生成角色創建令牌
AvatarCreationService:PromptCreateAvatarAsync() 需要一個 角色創建令牌 ID 參數。此令牌是您宇宙中創建項目的關鍵,您可以用來設置來自您的體驗的角色創建價格。 有關生成令牌的說明和更多詳細信息,請參見 角色創建令牌。
在購買並生成您的令牌後,您可以在創作者中心檢查該令牌以找到 ID,然後可以用於 AvatarCreationService:PromptCreateAvatarAsync() API。

根據歸因回應玩家加入
在體驗中創建的角色捆綁具有指向原始體驗的歸因鏈接,該體驗創建了角色。如果其他玩家檢查該角色,提示會顯示提供選項以訪問創建該角色的體驗。

要處理使用此歸因鏈接加入您的體驗的玩家,請使用 Player:GetJoinData() 並解析返回的表以獲得 GameJoinContext。
GameJoinContext 包含以下表值:
- JoinSource — Enum.JoinSource
- 來自此歸因鏈接加入您的體驗的 Player 將具有 Enum.JoinSource.CreatedItemAttribution 以指示來自創建的項目的進入。
- ItemType — 可選的 Enum.AvatarItemType
- AssetId — 可選的 string
- OutfitId — 可選的 string
- AssetType — 可選的 Enum.AssetType