{itemType}を作成
収益化
ポリシーとガイドライン

経験内でのアバター作成

*このコンテンツは、ベータ版のAI(人工知能)を使用して翻訳されており、エラーが含まれている可能性があります。このページを英語で表示するには、 こちら をクリックしてください。

プレイヤーがリアルタイムでアバターのボディを作成、カスタマイズ、および購入できる経験を公開することができます。購入されたこれらのカスタムボディは、プレイヤーの Roblox インベントリに直接保存され、他の経験でカスタムアバターを装備して着用することができます。

経験の所有者は、経験内でアバター作成を実装することで、アバターアイテムの クリエイター経験所有者 の両方として マーケットプレイスの手数料 の恩恵を受けます。経験内で作成されたアセットが検査されると、そのアイテムはそれが作成された元の経験へのリンクを提供します。

Roblox の Avatar Creator デモで、経過内での作成をテストできます。

経験内での作成を実装する方法

以下の指示とコードリファレンスを使用して、あなたの最初の経験内アバター作成プロジェクトを作成します。次の指示では、プレイヤーが公開前に修正とカスタマイズができる基本的なボディ Model を使用します。

始める前に、以下の内容をよく理解しておいてください。

  • アバターモデル — 次の実装では、Roblox の 15 パーツの仕様に従った基本ボディをインポートする必要があります。この Model は追加のユーザーによるカスタマイズや修正の基礎として機能します。
  • アバター作成トークン — アバター作成を実装する経験には、少なくとも 1 つの作成トークンが必要です。これらのトークンは Robux で購入する必要があり、経験内での購入に対して価格や他の販売設定を設定することができます。
  • API クラス
    • AvatarCreationService — アバター作成のプロンプトとバリデーションを処理します。
    • EditableImage — テクスチャのランタイム作成と操作を処理します。
    • EditableMesh — メッシュジオメトリのランタイム操作を処理します。
    • WrapDeformer — アバターキャラクターが 3D 服 を装備できるようにするための不可視の外骨格ジオメトリのランタイム操作を処理します。

基本ボディをインポートする

基本ボディは、ユーザーがカスタマイズおよび編集できる初期の基盤として機能します。独自の Model を使用するか、Importerを使用してカスタムアセットをインポートし、Avatar Setup 経由で設定できます。

基本ボディは、Roblox のアバター仕様に従わなければならず、頭部、胴体、左腕、左脚、右腕、右脚の 6 つのボディパーツを構成する 15 の MeshPart インスタンスや、その他の アバターコンポーネント を含める必要があります。

適切に構成されたアバターボディのリファレンスやサンプルについては、Avatar referencesを参照してください。

編集 API を実装する

ユーザーがあなたの経験内のアバターの MeshPart インスタンスを編集できるシステムを開発するには、テクスチャ編集用に EditableImage、メッシュ編集用に EditableMesh、メッシュ編集中にスキニングと FACS データを維持するために WrapDeformer を使用します。

  1. 基本ボディをインポートした後、以下のスクリプトを使用して EditableImagesEditableMeshesWrapDeformers を設定します。


    local AssetService = game:GetService("AssetService")
    local function setupBodyPart(meshPart, wrapTarget)
    -- Create and attach a WrapDeformer to the MeshPart
    local wrapDeformer = Instance.new("WrapDeformer")
    wrapDeformer.Parent = meshPart
    -- Create an editable mesh for the wrap target's cage mesh
    local cageEditableMesh: EditableMesh =
    AssetService:CreateEditableMeshAsync(Content.fromUri(wrapTarget.CageMeshId), {
    FixedSize = true,
    })
    -- Assign the cage mesh to the WrapDeformer
    wrapDeformer:SetCageMeshContent(Content.fromObject(cageEditableMesh))
    end
    local function setupRigidMesh(meshPart)
    -- Create an editable mesh from the original MeshPart
    local editableMesh = AssetService:CreateEditableMeshAsync(Content.fromUri(meshPart.MeshId), {
    FixedSize = true,
    })
    -- Generate a new MeshPart from the editable mesh
    local newMeshPart = AssetService:CreateMeshPartAsync(Content.fromObject(editableMesh))
    -- Copy size, position, and texture from the original MeshPart
    newMeshPart.Size = meshPart.Size
    newMeshPart.CFrame = meshPart.CFrame
    newMeshPart.TextureContent = meshPart.TextureContent
    -- Apply the new MeshPart back to the original
    meshPart:ApplyMesh(newMeshPart)
    end
    local function setupMeshTexture(meshPart, textureIdToEditableImageMap)
    -- If EditableImage already exists for this TextureID, reuse it rather than making a new one
    if textureIdToEditableImageMap[meshPart.TextureID] then
    meshPart.TextureContent =
    Content.fromObject(textureIdToEditableImageMap[meshPart.TextureID])
    return
    end
    -- Create a new EditableImage and apply it as the texture content
    local editableImage = AssetService:CreateEditableImageAsync(Content.fromUri(meshPart.TextureID))
    textureIdToEditableImageMap[meshPart.TextureID] = editableImage
    meshPart.TextureContent = Content.fromObject(editableImage)
    end
    local function setupModel(model)
    -- Map for reusing EditableImage instances by texture ID
    local textureIdToEditableImageMap = {}
    for _, descendant in model:GetDescendants() do
    if not descendant:IsA("MeshPart") then
    continue
    end
    -- Configure MeshPart based on WrapTarget presence
    -- If WrapTarget is present, add a WrapDeformer child with an EditableMesh
    -- Otherwise, apply EditableMesh to the MeshPart directly
    local wrapTarget = descendant:FindFirstChildOfClass("WrapTarget")
    if wrapTarget then
    setupBodyPart(descendant, wrapTarget)
    else
    setupRigidMesh(descendant)
    end
    -- Configure the EditableImage for the MeshPart
    setupMeshTexture(descendant, textureIdToEditableImageMap)
    end
    end
  2. EditableImage ツールを作成して、プレイヤーが基本ボディに色を付けたり、描画したり、ステッカーを追加できるようにします。DrawImage()DrawRectangle()WritePixelsBuffer() などの API を活用できます。

    • 高度な変換には、DrawImageTransformed() を使用して、1 つの EditableImage に別の EditableImage を描画する際の位置、回転、スケールを指定できます。同様に、DrawImageProjected()DrawImage() と非常に似ていますが、描画された画像を正しくプロジェクトします。


      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 は、基底のスキニングと FACS データを維持しつつ、レンダリングされた MeshPart ジオメトリのライブ変形を処理します。

    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 --Folder with Created 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("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 に、以下の 6 つのボディパーツのために新たに作成されるアセットを含める必要があります。 (Head, Torso, RightLeg, LeftLeg, RightArm, LeftArm)。オプションで、新しい Hair アクセサリーを含めることもできます。

これをサポートするために、HumanoidDescription には 6 つの BodyPartDescription 子供が含まれている必要があります。各 BodyPartDescription.Instance プロパティは、ボディパーツを構成するすべての MeshPart インスタンスを含む Folder を参照します。たとえば、LeftArm フォルダには LeftHandLeftUpperArmLeftLowerArm MeshParts が含まれています。 BodyPartDescription.BodyPart プロパティも、関連する Enum.BodyPart に設定されている必要があります。

15 の MeshPart ボディパーツそれぞれには以下を含める必要があります:

提供される HumanoidDescription には、創作対象のボディパーツやアクセサリーを表す既存のアセット ID を含めてはいけません。一方で、HumanoidDescription には BodyTypeScaleHeadScaleHeightScaleWidthScaleProportionScale のヒューマノイドスケールを含めることができます。ベースボディがインポートされるスケールと、HumanoidDescription に与えられるスケールが一致していることに注意してください。

アクセサリーを含める

髪の毛などのアクセサリーを含める場合、HumanoidDescription には子として AccessoryDescription を含める必要があります。

アバター作成トークンを生成する

AvatarCreationService:PromptCreateAvatarAsync()アバター作成トークン ID パラメータを必要とします。このトークンは、あなたのユニバースからの創作のためのキーであり、経験からアバター作成の価格を設定するために使用できます。トークンを生成する手順や詳細については、アバター作成トークンを参照してください。

トークンを購入し生成した後、クリエイターハブでトークンを検査して ID を見つけ、その ID を AvatarCreationService:PromptCreateAvatarAsync() API に使用できます。

帰属によるプレイヤーの参加に応答する

経験内で作成されたアバターバンドルには、アバターが作成された元の経験への帰属リンクが含まれています。他のプレイヤーがアバターを検査した場合、そのアバターが作成された経験を訪れるオプションを提供するプロンプトが表示されます。

この帰属リンクを使用してあなたの経験に参加するプレイヤーを処理するには、Player:GetJoinData() を使用し、返されたテーブルを GameJoinContext 用にパースします。

GameJoinContext には以下のテーブル値が含まれています:

©2026 Roblox Corporation。Roblox(ロブロックス)、RobloxロゴおよびPowering Imaginationは、米国並びにその他の国における登録商標および非登録商標です。