创建{itemType}
Accelerate
收益化
政策和指南

体验中的头像创建

*此内容使用人工智能(Beta)翻译,可能包含错误。若要查看英文页面,请点按 此处

您可以发布一种体验,允许玩家实时创建、自定义和购买头像身体。当购买后,这些自定义身体会直接保存到玩家的 Roblox 库中,使玩家能够在其他体验中装备和穿戴自定义头像。

实施体验内头像创建的体验所有者将受益于 市场佣金,作为头像物品的 创作者体验所有者。如果在体验中创建的资产被检查,该项物品会提供一个链接到其创建的原始体验。

您可以在 Roblox 的 头像创建器 演示中测试体验内创建。

如何实现体验内创建

请使用以下说明和代码参考,以创建您的第一个体验内头像创建项目。以下说明使用一个基本身体 Model,玩家可以在发布前进行修改和自定义。

在开始之前,请先熟悉以下内容:

  • 头像模型 — 以下实现需要导入符合 Roblox 15 部件规范的基本身体。该 Model 作为额外用户自定义和修改的基础。
    • 基本身体必须符合 Roblox 的 头像身体指南,包括面部装配所需的最小 FACS 控制点数量。
  • 头像创建令牌 — 实施头像创建的体验至少需要一个创建令牌。这些令牌需要 Robux 进行购买,并允许您为体验内的购买设置价格和其他销售设置。
  • API 类

导入基本身体

基本身体作为用户可以自定义和编辑的初始基础。您可以使用自己的 Model,或者通过 导入工具 导入自定义资产,并通过 头像设置 配置。

基本身体必须遵循 Roblox 的头像规格,并且必须包括如 15 个 MeshPart 实例组成的 6 个身体部件:头、躯干、左臂、左腿、右臂和右腿,以及其他 头像组件

有关正确配置的头像身体的参考和示例,请参见 头像参考

实现编辑 API

为了开发一个系统,使用户能够在您的体验中编辑头像上 MeshPart 实例,以进行创建,请使用 EditableImage 进行纹理编辑,EditableMesh 进行网格编辑,以及 WrapDeformer 在网格编辑期间维护皮肤和 FACS 数据。

  1. 导入基本身体后,请使用以下脚本设置您的 EditableImagesEditableMeshesWrapDeformers


    local AssetService = game:GetService("AssetService")
    local function setupBodyPart(meshPart, wrapTarget)
    -- 创建并将 WrapDeformer 附加到 MeshPart
    local wrapDeformer = Instance.new("WrapDeformer")
    wrapDeformer.Parent = meshPart
    -- 为包裹目标的笼子网格创建一个可编辑网格
    local cageEditableMesh: EditableMesh =
    AssetService:CreateEditableMeshAsync(Content.fromUri(wrapTarget.CageMeshId), {
    FixedSize = true,
    })
    -- 将笼子网格分配给 WrapDeformer
    wrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh))
    end
    local function setupRigidMesh(meshPart)
    -- 根据原始 MeshPart 创建可编辑网格
    local editableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri(meshPart.MeshId), {
    FixedSize = true,
    })
    -- 从可编辑网格生成一个新的 MeshPart
    local newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh))
    -- 复制原始 MeshPart 的大小、位置和纹理
    newMeshPart.Size = meshPart.Size
    newMeshPart.CFrame = meshPart.CFrame
    newMeshPart.TextureContent = meshPart.TextureContent
    -- 将新 MeshPart 应用回原始
    meshPart:ApplyMesh(newMeshPart)
    end
    local function setupMeshTexture(meshPart, textureIdToEditableImageMap)
    -- 如果此 TextureID 已存在 EditableImage,则重新使用它而不是创建新的
    if textureIdToEditableImageMap[meshPart.TextureID] then
    meshPart.TextureContent =
    Content.fromObject(textureIdToEditableImageMap[meshPart.TextureID])
    return
    end
    -- 创建一个新的 EditableImage 并将其应用为纹理内容
    local editableImage = AssetService:CreateEditableImageAsync(Content.fromUri(meshPart.TextureID))
    textureIdToEditableImageMap[meshPart.TextureID] = editableImage
    meshPart.TextureContent = Content.fromObject(editableImage)
    end
    local function setupModel(model)
    -- 根据纹理 ID 重用 EditableImage 实例的映射
    local textureIdToEditableImageMap = {}
    for _, descendant in model:GetDescendants() do
    if not descendant:IsA("MeshPart") then
    continue
    end
    -- 基于 WrapTarget 存在来配置 MeshPart
    -- 如果 WrapTarget 存在,则添加带有 EditableMesh 的 WrapDeformer 子项
    -- 否则,将 EditableMesh 直接应用于 MeshPart
    local wrapTarget = descendant:FindFirstChildOfClass("WrapTarget")
    if wrapTarget then
    setupBodyPart(descendant, wrapTarget)
    else
    setupRigidMesh(descendant)
    end
    -- 为 MeshPart 配置 EditableImage
    setupMeshTexture(descendant, textureIdToEditableImageMap)
    end
    end
  2. 创建 EditableImage 工具,允许玩家更改颜色、绘画或向基本身体添加贴纸。您可以利用像 DrawImage()DrawRectangle()WritePixelsBuffer() 的接口。

    • 对于高级变换,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
      )
      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. 使用 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 -- 文件夹,与创建的 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
end

AvatarCreationService:PromptCreateAvatarAsync() 接受一个 HumanoidDescription 参数,表示要购买或创建的头像。对于头像创建,角色的 HumanoidDescription 必须包括为每个 6 个身体部件创建的新资产( HeadTorsoRightLegLeftLegRightArmLeftArm)。可选择性地,它还可以包括一个新的 Hair 配件。

为了支持这一点,HumanoidDescription 应该包括 6 个 BodyPartDescription 子对象。每个 BodyPartDescription.Instance 属性引用一个包含所有 MeshPart 实例的 Folder,这些实例组成该身体部件。例如,LeftArm 文件夹包含 LeftHandLeftUpperArmLeftLowerArm MeshPartsBodyPartDescription.BodyPart 属性也应该设置为相关的 Enum.BodyPart

每个 15 个 MeshPart 身体部件必须包括:

提供的 HumanoidDescription 应该不包含任何现有资产 ID 来表示预期创建的身体部件或配件。另一方面,HumanoidDescription 可以包含 BodyTypeScaleHeadScaleHeightScaleWidthScaleProportionScale 的人形比例。请注意导入基础身体的比例,以便它们与提供给 HumanoidDescription 的比例相匹配。

包括配件

如果包括一个配件,例如头发,HumanoidDescription 应该包括一个子 AccessoryDescription,其中:

生成头像创建令牌

AvatarCreationService:PromptCreateAvatarAsync() 接受一个 头像创建令牌 ID 参数。此令牌是您宇宙创建的关键,您可以使用它来设置来自体验的头像创建价格。有关生成令牌的说明和更多详细信息,请参阅 头像创建令牌

在购买和生成您的令牌后,您可以在创作者中心检查该令牌以找到随后可用于 AvatarCreationService:PromptCreateAvatarAsync() API 的 ID。

通过归属响应玩家加入

在体验中创建的头像包包括指向原始创建该头像的体验的归属链接。如果其他玩家检查此头像,则会显示一个提示,提供访问创建该头像的体验的选项。

为处理使用该归属链接加入您体验的玩家,请使用 Player:GetJoinData() 并解析返回的表以获取 GameJoinContext

GameJoinContext 包含以下表值:

©2026 Roblox Corporation、Roblox、Roblox 标志及 Powering Imagination 是我们在美国及其他国家或地区的注册与未注册商标。