يمكنك نشر تجربة تسمح للاعبين بإنشاء وتخصيص وشراء أجساد الأفاتار في الوقت الفعلي. عند الشراء، يتم حفظ هذه الأجساد المخصصة مباشرة في مخزون اللاعب عبر Roblox، مما يسمح للاعبين بتجهيز وإرتداء الأفاتار المخصصة في تجارب أخرى.
يستفيد مالكو التجارب الذين يقومون بتطبيق إنشاء الأفاتار أثناء التجربة من عمولات السوق كـ مبدع عنصر الأفاتار ومالك التجربة. إذا تم فحص أحد الأصول التي تم إنشاؤها خلال التجربة، توفر العنصر رابطًا للتجربة الأصلية التي تم إنشاؤه فيها.
يمكنك اختبار إنشاء الأفاتار خلال التجربة في محرر الأفاتار من Roblox.
كيفية تطبيق إنشاء الأفاتار أثناء التجربة
استخدم التعليمات التالية ومراجع الكود لإنشاء مشروع إنشاء الأفاتار في تجاربك. التعليمات التالية تستخدم جسمًا أساسيًا Model يمكن للاعبين تعديله وتخصيصه قبل النشر.
قبل البدء، تعرف على ما يلي:
- نماذج الأفاتار — يتطلب تنفيذ التالي استيراد جسم أساسي يلتقي بمواصفات أجزاء Roblox المكونة من 15 جزءًا. هذا Model يعمل كأساس للتخصيص والتعديل الإضافي من قبل المستخدمين.
- يجب أن يلبي الجسم الأساسي إرشادات جسم الأفاتار من Roblox، بما في ذلك العدد الأدنى من عناصر التحكم FACS لتجهيز الوجه.
- رموز إنشاء الأفاتار — تتطلب التجارب التي تقوم بتطبيق إنشاء الأفاتار على الأقل رمزًا واحدًا لإنشاء. تتطلب هذه الرموز روبيز لشرائها وتسمح لك بتعيين الأسعار والإعدادات الأخرى للمبيعات التي تتم خلال التجربة.
- فئات API
- AvatarCreationService — يدير تحفيز وإنشاء الأفاتار والتحقق منها.
- EditableImage — يدير إنشاء وتعديل القوام أثناء التشغيل.
- EditableMesh — يدير تعديل هندسة الشبكة أثناء التشغيل.
- WrapDeformer — يدير تعديل هندسة القفص الخارجي غير المرئي الذي يسمح لشخصيات الأفاتار بتجهيز الملابس ثلاثية الأبعاد.
استيراد جسم أساسي
يعمل الجسم الأساسي كأساس أولي يمكن للمستخدمين تخصيصه وتعديله. يمكنك استخدام Model الخاص بك، أو استيراد أصول مخصصة باستخدام المستورد وإعدادها من خلال إعداد الأفاتار.
يجب أن تلتزم الأجسام الأساسية بمواصفات الأفاتار من Roblox، ويجب أن تتضمن مكونات مثل 15 من مثيلات MeshPart التي تشكل 6 أجزاء من الجسم: الرأس، الجذع، الذراع اليسرى، الساق اليسرى، الذراع اليمنى، والساق اليمنى، بالإضافة إلى مكونات الأفاتار الأخرى الأجزاء.
للحصول على المراجع والعينات للأجساد المهيأة بشكل صحيح، انظر مراجع الأفاتار.
تنفيذ واجهات برمجة التطبيقات للتعديل
لتطوير نظام حيث يمكن للمستخدمين تعديل 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,})-- توليد MeshPart جديدة من الشبكة القابلة للتعديلlocal 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)-- إذا كان EditableImage موجودًا بالفعل لهذا TextureID، استخدمه بدلاً من إنشاء جديد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)-- خريطة لإعادة استخدام مثيلات EditableImage بواسطة معرف القوامlocal textureIdToEditableImageMap = {}for _, descendant in model:GetDescendants() doif not descendant:IsA("MeshPart") thencontinueend-- تكوين MeshPart بناءً على وجود WrapTarget-- إذا كان WrapTarget موجودًا، أضف ابن WrapDeformer مع EditableMesh-- بخلاف ذلك، طبق EditableMesh مباشرة على MeshPartlocal wrapTarget = descendant:FindFirstChildOfClass("WrapTarget")if wrapTarget thensetupBodyPart(descendant, wrapTarget)elsesetupRigidMesh(descendant)end-- تكوين EditableImage لـ MeshPartsetupMeshTexture(descendant, textureIdToEditableImageMap)endendأنشئ أدوات 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)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.
- استخدم WrapDeformer:SetCageMeshContent() لتطبيق مثيل 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
إنشاء موجه لإنشاء الأفاتار
بعد إعداد جسمك الأساسي وواجهات برمجة التطبيقات للتعديل، أنشئ موجهًا للمستخدمين لإنشاء وشراء من التجربة باستخدام 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 الخاصة بالشخصية أصول جديدة يجب إنشاؤها لكل جزء من الأجزاء الستة (Head, Torso, RightLeg, LeftLeg, RightArm, LeftArm). اختيارياً، يمكن أن تتضمن أيضًا إكسسوارًا Hair جديدًا.
لدعم ذلك، يجب أن تتضمن HumanoidDescription 6 أطفال من BodyPartDescription. يجب أن تشير خاصية BodyPartDescription.Instance إلى Folder التي تتضمن جميع مثيلات MeshPart التي تشكل جزء الجسم. على سبيل المثال، يحتوي مجلد LeftArm على LeftHand وLeftUpperArm وLeftLowerArm MeshParts. يجب أيضًا تعيين خاصية BodyPartDescription.BodyPart إلى Enum.BodyPart المعني.
يجب أن تتضمن كل من 15 جزءًا من MeshPart ما يلي:
يجب ألا تتضمن HumanoidDescription المقدمة أي معرفات أصول موجودة مسبقًا لتمثيل أجزاء الجسم أو الإكسسوارات على الإنشاء المقصود. من ناحية أخرى، قد تتضمن HumanoidDescription مقاييس humanoid الخاصة بـ BodyTypeScale, HeadScale, HeightScale, WidthScale، و ProportionScale. يجب الانتباه إلى المقاييس التي تم استيراد جسم أساسي بها بحيث تتطابق مع المقاييس المقدمة إلى HumanoidDescription.

تضمين الإكسسوارات
إذا كنت تتضمن إكسسوارًا، مثل الشعر، يجب أن تتضمن HumanoidDescription طفل AccessoryDescription حيث:
- تشير خاصية AccessoryDescription.Instance إلى مثيل Accessory.
- يتم تعيين خاصية AccessoryDescription.AccessoryType إلى Enum.AccessoryType المعني.
- في حالة تضمين Enum.AccessoryType.Hair في إنشائاتك، يجب أن تتضمن MeshPart EditableImage. ومع ذلك، ينبغي ألا تتضمن WrapDeformer كطفل ولكن يجب أن تتضمن EditableMesh تم تعيينها مباشرة على MeshPart.
توليد رمز إنشاء الأفاتار
AvatarCreationService:PromptCreateAvatarAsync() تأخذ معلمة رمز إنشاء الأفاتار. هذا الرمز هو المفتاح لإنشاء من عالمك، وهو ما يمكنك استخدامه لتعيين سعر إنشاء الأفاتار من تجربتك. للحصول على التعليمات والتفاصيل الإضافية حول توليد الرموز، انظر رموز إنشاء الأفاتار.
بعد شراء وتوليد الرمز الخاص بك، يمكنك فحصه في مركز المبدعين للعثور على المعرف الذي يمكنك استخدامه بعد ذلك لـ AvatarCreationService:PromptCreateAvatarAsync() API.

الرد على انضمام اللاعبين بواسطة التخصيص
تشمل حزم الأفاتار التي تم إنشاؤها خلال التجربة رابط تخصيص للتجربة الأصلية التي تم إنشاء الأفاتار فيها. إذا تم فحص الأفاتار من قبل لاعب آخر، تظهر موجهة تقدم خيار زيارة التجربة حيث تم إنشاء الأفاتار.

للتعامل مع دخول اللاعبين إلى تجربتك باستخدام رابط التخصيص هذا، استخدم Player:GetJoinData() وحلل الجدول الذي تم إرجاعه للحصول على GameJoinContext.
يتضمن GameJoinContext قيم جدول التالية:
- JoinSource — Enum.JoinSource
- سيكون لدى Player الذي ينضم إلى تجربتك من هذا الرابط Enum.JoinSource.CreatedItemAttribution للإشارة إلى الدخول من عنصر تم إنشاؤه.
- ItemType — اختياري Enum.AvatarItemType
- AssetId — اختياري string
- OutfitId — اختياري string
- AssetType — اختياري Enum.AssetType