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

エクスペリエンス内アバター作成

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

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

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

Robloxのアバタークリエーターデモでエクスペリエンス内作成をテストできます。

エクスペリエンス内作成を実装する方法

以下の指示とコードリファレンスを使用して、最初のエクスペリエンス内アバター作成プロジェクトを作成します。以下の指示では、プレイヤーが公開前に変更およびカスタマイズできる基本ボディModelを使用します。

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

  • アバターモデル — 次の実装には、Robloxの15部品仕様を満たす基本ボディのインポートが必要です。このModelは、ユーザーの追加のカスタマイズと変更の基盤として機能します。
    • 基本ボディは、Robloxのアバターボディガイドラインを満たす必要があります。これには、顔のリギング用のFACSコントロールの最小数が含まれます。
  • アバター作成トークン — アバター作成を実装するエクスペリエンスには、少なくとも1つの作成トークンが必要です。これらのトークンは、購入するためにRobuxを必要とし、エクスペリエンス内での購入のための価格やその他の販売設定を設定することができます。
  • APIクラス
    • AvatarCreationService — アバター作成プロンプトとバリデーションを処理します。
    • EditableImage — テクスチャのランタイム作成と操作を処理します。
    • EditableMesh — メッシュジオメトリのランタイム操作を処理します。
    • WrapDeformer — アバターキャラクターが3D衣服を装備できるようにするための見えない外部ケージジオメトリのランタイム操作を処理します。

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

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

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

正しく設定されたアバターボディのリファレンスやサンプルについては、アバターリファレンスを参照してください。

編集APIを実装する

ユーザーがエクスペリエンス内でアバターのMeshPartインスタンスを編集できるシステムを開発するには、テクスチャ編集にEditableImage、メッシュ編集にEditableMesh、メッシュ編集中にスキニングおよびFACSデータを維持するにはWrapDeformerを使用します。

  1. 基本ボディをインポートした後、次のスクリプトを使用してEditableImagesEditableMeshes、およびWrapDeformersを設定します。

    local AssetService = game:GetService("AssetService")
    local function setupBodyPart(meshPart, wrapTarget)
    -- MeshPartにWrapDeformerを作成してアタッチ
    local wrapDeformer = Instance.new("WrapDeformer")
    wrapDeformer.Parent = meshPart
    -- WrapTargetのケージメッシュ用の編集可能なメッシュを作成
    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に適用
    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の子を追加
    -- そうでなければ、直接MeshPartにEditableMeshを適用
    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()などの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 --作成された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つのボディパーツそれぞれのために作成される新しいアセットが含まれている必要があります (Head, Torso, RightLeg, LeftLeg, RightArm, LeftArm). オプションで、新しいHairアクセサリーも含めることができます。

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

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

提供されたHumanoidDescriptionには、意図した作成に対してボディパーツやアクセサリーを表す既存のアセットIDは含めないでください。一方で、HumanoidDescriptionにはBodyTypeScaleHeadScaleHeightScaleWidthScale、および ProportionScaleのヒューマノイドスケールを含めることができます。基本ボディがインポートされる際のスケールが、HumanoidDescriptionに提供されるスケールと一致することを確認してください。

アクセサリーを含める

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

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

AvatarCreationService:PromptCreateAvatarAsync()は、アバター作成トークンIDパラメータを受け取ります。このトークンは、あなたのユニバースからの作成のキーであり、エクスペリエンスからのアバター作成の価格を設定するために使用できるものです。トークンの生成に関する指示および追加の詳細については、アバター作成トークンを参照してください。

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

出所に基づいてプレイヤーが参加することに応じて処理する

エクスペリエンス内で作成されたアバターバンドルには、アバターが作成された元のエクスペリエンスへの帰属リンクが含まれています。アバターが他のプレイヤーによって検査されると、そのアバターが作成されたエクスペリエンスに訪問するオプションを表示するプロンプトが表示されます。

この帰属リンクを使用してプレイヤーがあなたのエクスペリエンスに参加することに対応するには、Player:GetJoinData()を使用し、返されたテーブルをGameJoinContextとして解析します。

GameJoinContextには次のテーブルの値が含まれています。

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