البرنامج التعليمي: الواجهات والنماذج المخصصة

في هذا البرنامج التعليمي، تتعلم كيفية:

  • إضافة مجموعة أدوات Mixed Reality Toolkit إلى المشروع
  • إدارة حالة النموذج
  • تكوين Azure Blob Storage لاستيعاب النموذج
  • تحميل النماذج ومعالجتها للعرض

المتطلبات الأساسية

بدء استخدام مجموعة أدوات Mixed Reality Toolkit ‏(MRTK)

مجموعة أدوات Mixed Reality Toolkit ‏(MRTK) هي مجموعة عبر الأنظمة الأساسية لإنشاء تجارب الواقع المختلط. نستخدم MRTK 2.8.3 لميزات التفاعل والتصور الخاصة به.

يحتوي الدليل الرسمي لاستيراد MRTK على بعض الخطوات التي لا نحتاج إلى القيام بها. هذه الخطوات الثلاث فقط ضرورية:

  • استيراد الإصدار 2.8.3 من "Mixed Reality Toolkit/Mixed Reality Toolkit Foundation" إلى مشروعك من خلال أداة ميزة الحقيقة المختلطة (استيراد MRTK).
  • قم بتشغيل معالج تكوين MRTK (تكوين MRTK).
  • أضف MRTK إلى المشهد الحالي (إضافة إلى المشهد). استخدم ARRMixedRealityToolkitConfigurationProfile هنا بدلا من ملف التعريف المقترح في البرنامج التعليمي.

استيراد الأصول التي يستخدمها هذا البرنامج التعليمي

بدءا من هذا الفصل، سننفذ نمطا أساسيا لوحدة تحكم عرض النموذج بالنسبة إلى الكثير من المواد التي تغطيها. يمثل جزء model من النمط التعليمات البرمجية الخاصة بـ Azure Remote Rendering وإدارة الحالات المتعلقة بـ Azure Remote Rendering. يتم تنفيذ جزئي view وcontroller من النمط باستخدام أصول MRTK وبعض البرامج النصية المخصصة. من الممكن استخدام النموذج في هذا البرنامج التعليمي دون تنفيذ وحدة تحكم العرض هنا. يسمح لك هذا الفصل بدمج التعليمات البرمجية الموجودة في هذا البرنامج التعليمي بسهولة في التطبيق الخاص بك حيث يستحوذ على جزء وحدة تحكم العرض من نمط التصميم.

مع إدخال MRTK، هناك العديد من البرامج النصية والإعدادات الجاهزة والأصول التي يمكن إضافتها الآن إلى المشروع لدعم التفاعلات والملاحظات المرئية. يتم تجميع هذه الأصول المشار إليها باسم أصول البرنامج التعليمي في حزمة أصول Unity، والتي يتم تضمينها في Azure Remote Rendering GitHub في '\Unity\TutorialAssets\TutorialAssets.unitypackage'.

  1. استنسخ أو مستودع git‏ Azure Remote Rendering أو نزِّله، إذا كان التنزيل يؤدي إلى استخراج الملف المضغوط إلى موقع معروف.
  2. في مشروع Unity، اختر Assets -> Import Package -> Custom Package.
  3. في مستكشف الملفات، انتقل إلى الدليل حيث قمت باستنساخ مستودع Azure Remote Rendering أو فك ضغطه، ثم حدد .unitypackage الموجود في Unity -> TutorialAssets -> TutorialAssets.unitypackage
  4. حدد الزر Import لاستيراد محتويات الحزمة إلى المشروع.
  5. في محرر Unity، حدد Mixed Reality Toolkit -> Utilities -> Upgrade MRTK Standard Shader for Lightweight Render Pipeline من شريط القوائم العلوي واتبع المطالبات لترقية جهاز البكستر.

بمجرد إعداد MRTK وأصول البرنامج التعليمي للتحقق المزدوج، يتم تحديد ملف التعريف الصحيح.

  1. حدد GameObject‏ MixedRealityToolkit في التسلسل الهرمي للمشهد.
  2. في Inspector، ضمن مكون MixedRealityToolkit، بدِّل ملف تعريف التكوين إلى ARRMixedRealityToolkitConfigurationProfile.
  3. اضغط على Ctrl+S لحفظ التغييرات.

تقوم هذه الخطوة بتكوين MRTK، بشكل أساسي، مع ملفات تعريف HoloLens 2 الافتراضية. يتم تكوين ملفات التعريف المقدمة مسبقا بالطرق التالية:

  • إيقاف تشغيل محلل ملفات التعريف (اضغط 9 تشغيله/إيقاف تشغيله، أو قل "Show/Hide Profiler" على الجهاز).
  • إيقاف تشغيل مؤشر حملقة العين.
  • تمكين النقرات بالماوس في Unity بحيث يمكنك النقر على عناصر واجهة مستخدم MRTK باستخدام الماوس بدلاً من اليد التي تمت محاكاتها.

إضافة قائمة التطبيق

معظم وحدات التحكم في العرض في هذا البرنامج التعليمي تعمل على أساس فئات أساسية مجردة بدلاً من فئات مادية. يوفر هذا النمط مرونة أكبر ويسمح لنا بتوفير وحدات التحكم في العرض لك، ويظل يساعدك في معرفة التعليمات البرمجية لـ Azure Remote Rendering. للتبسيط، لا تحتوي فئة RemoteRenderingCoordinator على فئة مجردة مقدمة وتعمل وحدة تحكم العرض الخاصة بها مباشرة مقابل الفئة الملموسة.

يمكنك الآن إضافة AppMenu الجاهزة إلى المشهد، للحصول على ملاحظات مرئية لحالة الجلسة الحالية. يقدم AppMenu أيضا اللوحة المشروطة التي يستخدمها المستخدم لتخويل التطبيق للاتصال ب ARR.

  1. حدد موقع غرض AppMenu الجاهز في Assets/RemoteRenderingTutorial/Prefabs/AppMenu

  2. اسحب غرض AppMenu الجاهز في المشهد.

  3. إذا رأيت مربع حوار لمستورد TMP، فاتبع المطالبات لاستيراد أساسيات TMP. ثم أغلق مربع حوار المستورد، حيث لا توجد حاجة إلى الأمثلة والإضافات.

  4. يتم تكوين AppMenu لربط العنصر المشروط وتوفيره للموافقة على الاتصال بالجلسة، حتى نتمكن من إزالة التجاوز الموضوع في وقت سابق. في GameObject‏ RemoteRenderingCoordinator، أزل التجاوز للتخويل الذي نفذناه سابقا، عن طريق الضغط على زر '-' في حدث On Requesting Authorization.

    إزالة التجاوز.

  5. يمكنك اختبار وحدة التحكم في العرض عن طريق الضغط على Play في Unity Editor.

  6. في Editor، بعد تكوين MRTK، يمكنك استخدام مفاتيح WASD لتغيير موضع طريقة العرض والضغط مع الاستمرار على زر الماوس الأيمن + تحريك الماوس لتغيير اتجاه العرض. حاول "التحرك" حول المشهد قليلاً لتأخذ فكرة عن وحدات التحكم.

  7. على الجهاز، يمكنك رفع كفك لاستدعاء AppMenu، واستخدم مفتاح التشغيل السريع 'M' في Unity Editor.

  8. إذا لم تظهر القائمة، اضغط على مفتاح 'M' لاستدعاء القائمة. يتم وضع القائمة بالقرب من الكاميرا لسهولة التفاعل.

  9. يقدم AppMenu عنصر واجهة المستخدم للتخويل إلى يمين AppMenu. من الآن فصاعدا، يجب استخدام عنصر واجهة المستخدم هذا لتخويل التطبيق لإدارة جلسات العرض عن بعد.

    تخويل واجهة المستخدم

  10. أوقف تشغيل Unity لمتابعة البرنامج التعليمي.

إدارة حالة النموذج

نحتاج إلى برنامج نصي جديد يسمى RemoteRenderedModel مخصص لحالة التتبع والاستجابة للأحداث وإطلاق الأحداث والتكوين. بشكل أساسي، يخزن RemoteRenderedModel المسار البعيد للبيانات المشروطة في modelPath. يستمع إلى تغييرات الحالة في RemoteRenderingCoordinator لمعرفة ما إذا كان يجب تحميل النموذج الذي يحدده أو إلغاء تحميله تلقائيا. GameObject الذي يحتوي على RemoteRenderedModel المرفق به هو الأصل المحلي للمحتوى البعيد.

لاحظ أن برنامج RemoteRenderedModel النصي ينفذ BaseRemoteRenderedModel، المدرج من Tutorial Assets. يسمح هذا الاتصال لوحدة تحكم عرض النموذج البعيد بالربط مع البرنامج النصي الخاص بك.

  1. أنشئ برنامجاً نصياً جديداً يسمى RemoteRenderedModel في المجلد نفسه مثل RemoteRenderingCoordinator. استبدل المحتويات بالتعليمات البرمجية التالية:

    // Copyright (c) Microsoft Corporation. All rights reserved.
    // Licensed under the MIT License. See LICENSE in the project root for license information.
    
    using Microsoft.Azure.RemoteRendering;
    using Microsoft.Azure.RemoteRendering.Unity;
    using System;
    using UnityEngine;
    using UnityEngine.Events;
    
    public class RemoteRenderedModel : BaseRemoteRenderedModel
    {
        public bool AutomaticallyLoad = true;
    
        private ModelState currentModelState = ModelState.NotReady;
    
        [SerializeField]
        [Tooltip("The friendly name for this model")]
        private string modelDisplayName;
        public override string ModelDisplayName { get => modelDisplayName; set => modelDisplayName = value; }
    
        [SerializeField]
        [Tooltip("The URI for this model")]
        private string modelPath;
        public override string ModelPath
        {
            get => modelPath.Trim();
            set => modelPath = value;
        }
    
        public override ModelState CurrentModelState
        {
            get => currentModelState;
            protected set
            {
                if (currentModelState != value)
                {
                    currentModelState = value;
                    ModelStateChange?.Invoke(value);
                }
            }
        }
    
        public override event Action<ModelState> ModelStateChange;
        public override event Action<float> LoadProgress;
        public override Entity ModelEntity { get; protected set; }
    
        public UnityEvent OnModelNotReady = new UnityEvent();
        public UnityEvent OnModelReady = new UnityEvent();
        public UnityEvent OnStartLoading = new UnityEvent();
        public UnityEvent OnModelLoaded = new UnityEvent();
        public UnityEvent OnModelUnloading = new UnityEvent();
    
        public UnityFloatEvent OnLoadProgress = new UnityFloatEvent();
    
        public void Awake()
        {
            // Hook up the event to the Unity event
            LoadProgress += (progress) => OnLoadProgress?.Invoke(progress);
    
            ModelStateChange += HandleUnityStateEvents;
        }
    
        private void HandleUnityStateEvents(ModelState modelState)
        {
            switch (modelState)
            {
                case ModelState.NotReady:  OnModelNotReady?.Invoke();  break;
                case ModelState.Ready:     OnModelReady?.Invoke();     break;
                case ModelState.Loading:   OnStartLoading?.Invoke();   break;
                case ModelState.Loaded:    OnModelLoaded?.Invoke();    break;
                case ModelState.Unloading: OnModelUnloading?.Invoke(); break;
            }
        }
    
        private void Start()
        {
            //Attach to and initialize current state (in case we're attaching late)
            RemoteRenderingCoordinator.CoordinatorStateChange += Instance_CoordinatorStateChange;
            Instance_CoordinatorStateChange(RemoteRenderingCoordinator.instance.CurrentCoordinatorState);
        }
    
        /// <summary>
        /// Listen for state changes on the coordinator, clean up this model's remote objects if we're no longer connected.
        /// Automatically load if required
        /// </summary>
        private void Instance_CoordinatorStateChange(RemoteRenderingCoordinator.RemoteRenderingState state)
        {
            switch (state)
            {
                case RemoteRenderingCoordinator.RemoteRenderingState.RuntimeConnected:
                    CurrentModelState = ModelState.Ready;
                    if (AutomaticallyLoad)
                        LoadModel();
                    break;
                default:
                    UnloadModel();
                    break;
            }
        }
    
        private void OnDestroy()
        {
            RemoteRenderingCoordinator.CoordinatorStateChange -= Instance_CoordinatorStateChange;
            UnloadModel();
        }
    
        /// <summary>
        /// Asks the coordinator to create a model entity and listens for coordinator state changes
        /// </summary>
        [ContextMenu("Load Model")]
        public override async void LoadModel()
        {
            if (CurrentModelState != ModelState.Ready)
                return; //We're already loaded, currently loading, or not ready to load
    
            CurrentModelState = ModelState.Loading;
    
            ModelEntity = await RemoteRenderingCoordinator.instance?.LoadModel(ModelPath, this.transform, SetLoadingProgress);
    
            if (ModelEntity != null)
                CurrentModelState = ModelState.Loaded;
            else
                CurrentModelState = ModelState.Error;
        }
    
        /// <summary>
        /// Clean up the local model instances
        /// </summary>
        [ContextMenu("Unload Model")]
        public override void UnloadModel()
        {
            CurrentModelState = ModelState.Unloading;
    
            if (ModelEntity != null)
            {
                var modelGameObject = ModelEntity.GetOrCreateGameObject(UnityCreationMode.DoNotCreateUnityComponents);
                Destroy(modelGameObject);
                ModelEntity.Destroy();
                ModelEntity = null;
            }
    
            if (RemoteRenderingCoordinator.instance.CurrentCoordinatorState == RemoteRenderingCoordinator.RemoteRenderingState.RuntimeConnected)
                CurrentModelState = ModelState.Ready;
            else
                CurrentModelState = ModelState.NotReady;
        }
    
        /// <summary>
        /// Update the Unity progress event
        /// </summary>
        /// <param name="progressValue"></param>
        public override void SetLoadingProgress(float progressValue)
        {
            LoadProgress?.Invoke(progressValue);
        }
    }
    

في أبسط المصطلحات، يحتفظ RemoteRenderedModel بالبيانات المطلوبة لتحميل نموذج (في هذه الحالة معرف الموارد المنتظم (URI) لـ SAS أو builtin://‎) ويتتبع حالة النموذج البعيد. عندما حان الوقت لتحميل النموذج، LoadModel يتم استدعاء الأسلوب على RemoteRenderingCoordinator، ويتم إرجاع الكيان الذي يحتوي على النموذج للرجوع إليه وتفريغه.

تحميل نموذج الاختبار

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

  1. أنشئ GameObject جديداً في المشهد وأطلق عليه اسم ModelStage.

  2. إضافة مكون WorldAnchor إلى ModelStage

    إضافة مكون WorldAnchor

  3. أنشئ GameObject فارغاً جديداً كتابع لـ ModelStage وأطلق عليه اسم TestModel.

  4. أضف برنامج RemoteRenderedModel النصي إلى TestModel.

    إضافة مكون RemoteRenderedModel

  5. املأ كلاً من Model Display Name وModel Path بـ "TestModel" و"builtin://Engine" على التوالي.

    تحديد تفاصيل النموذج

  6. ضع كائن TestModel أمام الكاميرا، في الموضع x = 0‏، y = 0‏، z = 3.

    وضع الكائن

  7. تأكد من تشغيل AutomaticallyLoad.

  8. اضغط على Play في Unity Editor لاختبار التطبيق.

  9. امنح التخويل بالنقر فوق الزر Connect للسماح للتطبيق بإنشاء جلسة عمل والاتصال بها وتحميل النموذج تلقائيا.

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

حاول نقل TestModel GameObject وتدويره عبر Transform في Inspector، أو في طريقة عرض Scene ولاحظ التحويلات في طريقة عرض Game.

سجل Unity

توفير مخزن الكائنات الثنائية كبيرة الحجم في Azure واستيعاب النموذج المخصص

الآن يمكننا أن نحاول تحميل نموذجك الخاص. للقيام بذلك، تحتاج إلى تكوين Blob Storage على Azure، وتحميل نموذج وتحويله، ثم تحميل النموذج باستخدام البرنامج النصي RemoteRenderedModel . يمكن تخطي خطوات تحميل النموذج المخصص بأمان إذا لم يكن لديك نموذجك الخاص لتحميله في هذا الوقت.

اتبع الخطوات المحددة في التشغيل السريع: تحويل نموذج للعرض. تخطي قسم إدراج نموذج جديد في تطبيق نموذج التشغيل السريع لهذا البرنامج التعليمي. بمجرد أن يكون لديك عنوان URI لتوقيع الوصول المشترك (SAS) الخاص بالنموذج الذي تم استيعابه، تابع.

تحميل نموذج مخصص وعرضه

  1. أنشئ GameObject فارغاً جديداً في المشهد وأطلق عليه اسماً مشابهاً للنموذج المخصص.

  2. أضف برنامج RemoteRenderedModel النصي إلى GameObject المنشأ حديثاً.

    إضافة مكون RemoteRenderedModel

  3. املأ Model Display Name باسم مناسب للنموذج.

  4. Model Path املأ ب URI توقيع الوصول المشترك (SAS) الخاص بالنموذج الذي أنشأته في توفير تخزين كائن ثنائي كبير الحجم في Azure وخطوة استيعاب النموذج المخصص.

  5. ضع GameObject أمام الكاميرا، في الموضع x = 0،‏ y = 0،‏ z = 3.

  6. تأكد من تشغيل AutomaticallyLoad.

  7. اضغط على Play في Unity Editor لاختبار التطبيق.

    تعرض وحدة التحكم حالة جلسة العمل الحالية وأيضا رسائل تقدم تحميل النموذج، بمجرد توصيل الجلسة.

  8. أزِل كائن النموذج المخصص من المشهد. أفضل تجربة لهذا البرنامج التعليمي هي مع نموذج الاختبار. بينما يتم دعم نماذج متعددة في ARR، تمت كتابة هذا البرنامج التعليمي لدعم نموذج بعيد واحد على أفضل نحو في كل مرة.

الخطوات التالية

يمكنك الآن تحميل نماذجك الخاصة في Azure Remote Rendering وعرضها في تطبيقك! بعد ذلك، نرشدك خلال معالجة نماذجك.