경험 내 아바타 작품

*이 콘텐츠는 AI(베타)를 사용해 번역되었으며, 오류가 있을 수 있습니다. 이 페이지를 영어로 보려면 여기를 클릭하세요.

플레이어가 실시간으로 아바타 바디를 생성, 사용자 정의 및 구매할 수 있는 경험을 게시할 수 있습니다.구매하면 이러한 사용자 지정 바디는 플레이어의 Roblox 인벤토리에 직접 저장되어 플레이어가 다른 경험에서 사용자 지정 아바타를 장착하고 착용할 수 있습니다.

경험 내 아바타 생성을 구현하는 경험 소유자는 마켓플레이스 수수료(크리에이터 및 경험 소유자)를 두 가지 측면에서 활용합니다.경험 내에서 생성된 자산이 검사되면 항목은 원래 경험에 대한 링크를 제공합니다.

Roblox의 아바타 크리에이터 데모에서 경험 내 생성을 테스트할 수 있습니다.

경험 내 생성 작품방법

다음 지침과 코드 참조를 사용하여 첫 번째 경험 내 아바타 생성 프로젝트를 만듭니다.다음 지침에서는 게시하기 전에 플레이어가 수정하고 사용자 정의할 수 있는 기본 바디 Model를 사용합니다.

시작하기 전에 다음에 익숙해지세요:

  • 아바타 모델 — 다음 구현에는 Roblox의 15부 사양에 맞는 기본 바디를 가져오는 것이 필요합니다.이 Model 는 추가 사용자 사용자 정의 및 수정을 위한 기반으로 사용됩니다.
  • 아바타 생성 토큰 — 아바타 생성 경험에는 최소 하나의 생성 토큰이 필요합니다.이 토큰은 Robux를 구매하고 경험 내에서 이루어진 구매에 대한 가격 및 기타 판매 설정을 설정할 수 있도록 해야 합니다.
  • API 클래스
    • AvatarCreationService — 아바타 생성 요청 및 유효성 검사를 처리합니다.
    • EditableImage — 텍스처의 런타임 생성 및 조작을 처리합니다.
    • EditableMesh — 메쉬 기하구조의 런타임 조작을 처리합니다.
    • WrapDeformer — 아바타 캐릭터가 3D 의류를 장착할 수 있도록 하는 투명한 외부 철갑 기하학의 런타임 조작을 처리합니다.

기본 신체가져오기

기본 바디는 사용자가 사용자 지정하고 편집할 수 있는 초기 기반으로 작동합니다.자신의 Model 를 사용하거나 3D 가져오기 와 함께 사용자 지정 자산을 가져와 아바타 설정을 통해 설정할 수 있습니다.

기본 바디는 Roblox의 아바타 사양을 준수해야 하며, 6개의 신체 부위(머리, 몸통, 왼쪽 팔, 왼쪽 다리, 오른쪽 팔, 오른쪽 다리)와 다른 아바타 구성 요소(5개)를 포함해야 합니다.

올바르게 구성된 아바타 바디의 참조 및 샘플은 아바타 참조에 참조하십시오.

편집 API 구현

경험에서 사용자가 작품위해 아바타의 MeshPart 인스턴스를 편집할 수 있는 시스템을 개발하려면 텍스처 편집을 위해 EditableImage 를 사용하고, 메쉬 편집에서 메쉬를 편집하기 위해 EditableMesh 을 사용하고, 피부 및 FACS 데이터를 유지하기 위해 WrapDeformer 을 사용합니다.

  1. 기본 신체가져온 후에는 다음 스크립트를 사용하여 EditableImages , EditableMeshes , 및 WrapDeformers 을 설정합니다.


    local AssetService = game:GetService("AssetService")
    local function setupBodyPart(meshPart, wrapTarget)
    -- 메시 파트에 WrapDeformer 생성 및 연결
    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,
    })
    -- 편집 가능한 메시에서 새로운 메시 파트 생성
    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)
    -- 이 텍스처ID에 편집 가능한 이미지가 이미 존재하는 경우 새로 만들기보다는 재사용하십시오
    if textureIdToEditableImageMap[meshPart.TextureID] then
    meshPart.TextureContent =
    Content.fromObject(textureIdToEditableImageMap[meshPart.TextureID])
    return
    end
    -- 새로운 편집 가능한 이미지를 만들고 텍스처 콘텐츠로 적용
    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 존재에 따라 메시 파트 구성
    -- WrapTarget가 있으면 편집 가능한 메쉬를 가진 WrapDeformer 자식을 추가합니다
    -- 그렇지 않으면 메시 파트에 편집 가능한 메시를 직접 적용하십시오
    local wrapTarget = descendant:FindFirstChildOfClass("WrapTarget")
    if wrapTarget then
    setupBodyPart(descendant, wrapTarget)
    else
    setupRigidMesh(descendant)
    end
    -- 메쉬 파트에 편집 가능한 이미지 구성
    setupMeshTexture(descendant, textureIdToEditableImageMap)
    end
    end
  2. 플레이어가 기본 신체색상을 바꾸고, 그리기 또는 스티커를 추가할 수 있는 EditableImageAPI를 활용할 수 있습니다 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
      )
      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 = (game.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 --만들어진 메쉬 파트가 있는 폴더
}
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 속성은 신체 부분을 구성하는 모든 Folder 인스턴스를 포함하는 MeshPart를 참조합니다.예를 들어, LeftArm 폴더에는 LeftHand , LeftUpperArm , LeftLowerArmMeshParts 가 포함되어 있습니다.BodyPartDescription.BodyPart 속성도 관련 Enum.BodyPart 로 설정해야 합니다.

15개의 MeshPart 신체 부위 각각에는 포함되어야 합니다:

제공된 HumanoidDescription는 의도된 작품신체 부위나 액세서리를 나타내기 위한 기존 자산 ID를 포함해서는 안됩니다.반면에, HumanoidDescription 는 인간형 저울의 BodyTypeScale , HeadScale , HeightScale , WidthScale , 및 ProportionScale 을 포함할 수 있습니다.기본 바디가 가져오는 비율을 신중하게 고려하여 제공된 비율과 일치하도록 합니다.Be mindful of the scales that a base body is imported with so that they match the scales provided to the HumanoidDescription .

액세서리 포함

헤어와 같은 장신구포함하는 경우, HumanoidDescription 는 다음과 같은 자식 AccessoryDescription 을 포함해야 합니다.

아바타 생성 토큰 생성Generate an avatar creation token

AvatarCreationService:PromptCreateAvatarAsync()아바타 생성 토큰 ID 매개변수를 사용합니다.이 토큰은 우주에서 창조물의 열쇠이며, 경험에서 아바타 생성 가격을 설정하는 데 사용할 수 있는 것입니다.토큰 생성에 대한 지침과 추가 세부 정보는 아바타 생성 토큰을 참조하십시오.

토큰을 구매하고 생성한 후 크리에이터 허브에서 토큰을 검사하여 AvatarCreationService:PromptCreateAvatarAsync()에 사용할 수 있는 ID를 찾을 수 있습니다.

공헌도에 따라 플레이어가 참여하도록 응답

경험 내에서 생성된 아바타 패키지에는 아바타가 생성된 원래 경험에 대한 공헌 링크가 포함됩니다.아바타가 다른 플레이어에 의해 검사되면 아바타가 생성된 경험을 방문할 수 있는 옵션을 제공하는 메시지가 표시됩니다.

이 공헌도 연결사용하여 경험에 참여하는 플레이어를 처리하려면 Player:GetJoinData()를 사용하고 반환된 테이블을 분석하십시오 GameJoinContext.

GameJoinContext 은 다음 테이블 값을 포함합니다: