كيفية بناء لعبة عداء لا نهاية لها في الواقع الافتراضي (الجزء 3)

نشرت: 2022-03-10
ملخص سريع ↬ في الجزء الأول ، شرح ألفين أساسيات كيفية تصميم نموذج واقع افتراضي. في الجزء الثاني ، أظهر كيفية تنفيذ منطق اللعبة الأساسي. في هذا الجزء الأخير من البرنامج التعليمي الخاص به ، ستتم إضافة اللمسات الأخيرة مثل قائمتي "Start" و "Game Over" بالإضافة إلى مزامنة حالات اللعبة بين عملاء الأجهزة المحمولة وأجهزة سطح المكتب. هذا يمهد الطريق للمفاهيم في بناء ألعاب متعددة اللاعبين.

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

ملاحظة : يمكن لعب هذه اللعبة مع أو بدون سماعة رأس VR. يمكنك مشاهدة العرض التوضيحي للمنتج النهائي على ergo-3.glitch.me.

للبدء ، سوف تحتاج إلى ما يلي.

  • الوصول إلى الإنترنت (على وجه التحديد لموقع glitch.com) ؛
  • اكتمل مشروع Glitch من الجزء 2 من هذا البرنامج التعليمي. يمكنك البدء من الجزء 2 من المنتج النهائي بالانتقال إلى https://glitch.com/edit/#!/ergo-2 والنقر فوق "ريميكس للتحرير" ؛
  • سماعة رأس للواقع الافتراضي (اختيارية ، موصى بها). (أستخدم Google Cardboard ، والذي يتم تقديمه بسعر 15 دولارًا للقطعة.)

الخطوة 1: عرض النتيجة

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

المزيد بعد القفز! أكمل القراءة أدناه ↓

في الواقع الافتراضي بشكل عام ، يتم دمج واجهة المستخدم بشكل أفضل في العالم بدلاً من تعليقها على رأس المستخدم.

عرض النتيجة
عرض النتيجة (معاينة كبيرة)

ابدأ بإضافة الكائن إلى index.html . أضف مزيجًا text ، والذي سيعاد استخدامه لعناصر نصية أخرى:

 <a-assets> ... <a-mixin text=" font:exo2bold; anchor:center; align:center;"></a-mixin> ... </a-assets>

بعد ذلك ، أضف عنصرًا text إلى النظام الأساسي ، قبل المشغل مباشرةً:

 <!-- Score --> <a-text value="" mixin="text" height="40" width="40" position="0 1.2 -3" opacity="0.75"></a-text> <!-- Player --> ...

هذا يضيف كيانًا نصيًا إلى مشهد الواقع الافتراضي. النص غير مرئي حاليًا ، لأنه تم تعيين قيمته على فارغ. ومع ذلك ، ستقوم الآن بتعبئة كيان النص ديناميكيًا باستخدام JavaScript. انتقل إلى الأصول / ergo.js. بعد قسم collisions ، أضف قسم score وحدد عددًا من المتغيرات العامة:

  • score : نتيجة المباراة الحالية.
  • countedTrees : معرفات جميع الأشجار التي تم تضمينها في النتيجة. (هذا لأن اختبارات التصادم قد يتم تشغيلها عدة مرات لنفس الشجرة.)
  • scoreDisplay : إشارة إلى كائن DOM ، يتوافق مع كائن نصي في عالم الواقع الافتراضي.
 /********* * SCORE * *********/ var score; var countedTrees; var scoreDisplay;

بعد ذلك ، حدد وظيفة الإعداد لتهيئة المتغيرات العامة لدينا. في نفس السياق ، حدد وظيفة teardown .

 ... var scoreDisplay; function setupScore() { score = 0; countedTrees = new Set(); scoreDisplay = document.getElementById('score'); } function teardownScore() { scoreDisplay.setAttribute('value', ''); }

في قسم Game ، قم بتحديث gameOver و startGame و window.onload لتضمين إعداد النتيجة وتفكيكها.

 /******** * GAME * ********/ function gameOver() { ... teardownScore(); } function startGame() { ... setupScore(); addTreesRandomlyLoop(); } window.onload = function() { setupScore(); ... }

حدد وظيفة تزيد الدرجة لشجرة معينة. ستتحقق هذه الوظيفة من countedTrees للتأكد من أن الشجرة لا يتم احتسابها مرتين.

 function addScoreForTree(tree_id) { if (countedTrees.has(tree_id)) return; score += 1; countedTrees.add(tree_id); }

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

 function updateScoreDisplay() { scoreDisplay.setAttribute('value', score); }

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

 AFRAME.registerComponent('player', { tick: function() { document.querySelectorAll('.tree').forEach(function(tree) { ... if (position.z > POSITION_Z_LINE_END) { addScoreForTree(tree_id); updateScoreDisplay(); } }) } })

أخيرًا ، قم بتحديث عرض النتيجة بمجرد بدء اللعبة. انتقل إلى قسم Game ، وأضف updateScoreDisplay(); startGame :

 function startGame() { ... setupScore(); updateScoreDisplay(); ... }

تأكد من أن الأصول / ergo.js و index.html تتطابق مع ملفات شفرة المصدر المقابلة. ثم انتقل إلى المعاينة الخاصة بك. يجب أن ترى ما يلي:

عرض النتيجة
عرض النتيجة (معاينة كبيرة)

هذا يختتم عرض النتيجة. بعد ذلك ، سنضيف قوائم البداية المناسبة وقائمة Game Over ، حتى يتمكن اللاعب من إعادة تشغيل اللعبة كما يشاء.

الخطوة 2: إضافة قائمة ابدأ

الآن بعد أن تمكن المستخدم من تتبع التقدم ، ستضيف اللمسات الأخيرة لإكمال تجربة اللعبة. في هذه الخطوة ، ستضيف قائمة ابدأ وقائمة Game Over ، مما يتيح للمستخدم بدء الألعاب وإعادة تشغيلها.

لنبدأ بقائمة ابدأ حيث ينقر اللاعب على زر "ابدأ" لبدء اللعبة. في النصف الثاني من هذه الخطوة ، ستضيف قائمة Game Over ، مع زر "إعادة التشغيل":

ابدأ واللعب على القوائم
ابدأ واللعب فوق القوائم (معاينة كبيرة)

انتقل إلى index.html في المحرر الخاص بك. ثم ابحث عن قسم Mixins . هنا ، قم بإلحاق مزيج title ، الذي يعرّف أنماط النص الكبير بشكل خاص. نحن نستخدم نفس الخط كما في السابق ، ونحاذي النص مع المركز ، ونحدد الحجم المناسب لنوع النص. (لاحظ أدناه أن نقطة anchor هي المكان الذي يتم فيه إرساء كائن النص في موضعه.)

 <a-assets> ... <a-mixin text=" font:exo2bold; height:40; width:40; opacity:0.75; anchor:center; align:center;"></a-mixin> </a-assets>

بعد ذلك ، أضف مزيجًا ثانيًا للعناوين الثانوية. هذا النص أصغر قليلاً ولكنه مطابق للعنوان.

 <a-assets> ... <a-mixin text=" font:exo2bold; height:10; width:10; opacity:0.75; anchor:center; align:center;"></a-mixin> </a-assets>

للخليط الثالث والأخير ، حدد خصائص النص الوصفي - حتى أصغر من العناوين الثانوية.

 <a-assets> ... <a-mixin text=" font:exo2bold; height:5; width:5; opacity:0.75; anchor:center; align:center;"></a-mixin> </a-assets>

مع تحديد جميع أنماط النص ، ستقوم الآن بتعريف كائنات النص الموجودة في العالم. أضف قسم Menus جديدًا أسفل قسم Score ، مع حاوية فارغة لقائمة ابدأ :

 <!-- Score --> ... <!-- Menus --> <a-entity> <a-entity position="0 1.1 -3"> </a-entity> </a-entity>

داخل حاوية قائمة البداية ، حدد العنوان والحاوية لجميع النصوص التي ليس لها عنوان:

 ... <a-entity ...> <a-entity position="0 1 0"> </a-entity> <a-text value="ERGO" mixin="title"></a-text> </a-entity> </a-entity>

داخل الحاوية للنص بدون عنوان ، أضف تعليمات للعب اللعبة:

 <a-entity...> <a-text value="Turn left and right to move your player, and avoid the trees!" mixin="copy"></a-text> </a-entity>

لإكمال قائمة " ابدأ " ، أضف زرًا نصه "ابدأ":

 <a-entity...> ... <a-text value="Start" position="0 0.75 0" mixin="heading"></a-text> <a-box position="0 0.65 -0.05" width="1.5" height="0.6" depth="0.1"></a-box> </a-entity>

تحقق مرة أخرى من أن كود HTML الخاص بقائمة ابدأ يطابق ما يلي:

 <!-- Menus --> <a-entity> <a-entity position="0 1.1 -3"> <a-entity position="0 1 0"> <a-text value="Turn left and right to move your player, and avoid the trees!" mixin="copy"></a-text> <a-text value="Start" position="0 0.75 0" mixin="heading"></a-text> <a-box position="0 0.65 -0.05" width="1.5" height="0.6" depth="0.1"></a-box> </a-entity> <a-text value="ERGO" mixin="title"></a-text> </a-entity> </a-entity>

انتقل إلى المعاينة الخاصة بك ، وسترى قائمة ابدأ التالية:

صورة قائمة ابدأ
قائمة ابدأ (معاينة كبيرة)

لا يزال في قسم Menus (أسفل قائمة start مباشرة) ، أضف قائمة game-over باستخدام نفس المزيجات:

 <!-- Menus --> <a-entity> ... <a-entity position="0 1.1 -3"> <a-text value="?" mixin="heading" position="0 1.7 0"></a-text> <a-text value="Score" mixin="copy" position="0 1.2 0"></a-text> <a-entity> <a-text value="Restart" mixin="heading" position="0 0.7 0"></a-text> <a-box position="0 0.6 -0.05" width="2" height="0.6" depth="0.1"></a-box> </a-entity> <a-text value="Game Over" mixin="title"></a-text> </a-entity> </a-entity>

انتقل إلى ملف JavaScript ، الأصول / ergo.js. قم بإنشاء قسم Menus جديد قبل قسم Game . بالإضافة إلى ذلك ، حدد ثلاث وظائف فارغة: setupAllMenus و hideAllMenus و showGameOverMenu .

 /******** * MENU * ********/ function setupAllMenus() { } function hideAllMenus() { } function showGameOverMenu() { } /******** * GAME * ********/

بعد ذلك ، قم بتحديث قسم Game في ثلاثة أماكن. في gameOver ، اعرض قائمة Game Over :

 function gameOver() { ... showGameOverMenu(); } ``` In `startGame`, hide all menus: ``` function startGame() { ... hideAllMenus(); }

بعد ذلك ، في window.onload ، قم بإزالة الاستدعاء المباشر لبدء setupAllMenus واستدعاء startGame بدلاً من ذلك. قم بتحديث المستمع الخاص بك لمطابقة ما يلي:

 window.onload = function() { setupAllMenus(); setupScore(); setupTrees(); }

انتقل مرة أخرى إلى قسم Menu . حفظ المراجع لكائنات DOM المختلفة:

 /******** * MENU * ********/ var menuStart; var menuGameOver; var menuContainer; var isGameRunning = false; var startButton; var restartButton; function setupAllMenus() { menuStart = document.getElementById('start-menu'); menuGameOver = document.getElementById('game-over'); menuContainer = document.getElementById('menu-container'); startButton = document.getElementById('start-button'); restartButton = document.getElementById('restart-button'); }

بعد ذلك ، اربط كلاً من زري "ابدأ" و "إعادة التشغيل" startGame :

 function setupAllMenus() { ... startButton.addEventListener('click', startGame); restartButton.addEventListener('click', startGame); }

حدد showStartMenu من setupAllMenus :

 function setupAllMenus() { ... showStartMenu(); } function hideAllMenus() { } function showGameOverMenu() { } function showStartMenu() { }

لتعبئة الوظائف الثلاث الفارغة ، ستحتاج إلى بعض الوظائف المساعدة. حدد الوظيفتين التاليتين ، اللتين تقبلان عنصر DOM يمثل كيان A-Frame VR ويظهره أو يخفيه. حدد الوظيفتين أعلاه showAllMenus :

 ... var restartButton; function hideEntity(el) { el.setAttribute('visible', false); } function showEntity(el) { el.setAttribute('visible', true); } function showAllMenus() { ...

قم أولاً بتعبئة hideAllMenus . ستزيل العناصر عن الأنظار ، ثم تزيل مستمعات النقر لكلتا القائمتين:

 function hideAllMenus() { hideEntity(menuContainer); startButton.classList.remove('clickable'); restartButton.classList.remove('clickable'); }

ثانيًا ، قم بتعبئة showGameOverMenu . هنا ، قم باستعادة الحاوية لكلا القائمتين ، بالإضافة إلى قائمة Game Over ومستمع النقر فوق الزر "إعادة التشغيل". ومع ذلك ، قم بإزالة مستمع النقر فوق الزر "ابدأ" ، وإخفاء قائمة "ابدأ".

 function showGameOverMenu() { showEntity(menuContainer); hideEntity(menuStart); showEntity(menuGameOver); startButton.classList.remove('clickable'); restartButton.classList.add('clickable'); }

ثالثًا ، قم بملء showStartMenu . هنا ، قم بعكس جميع التغييرات التي showGameOverMenu .

 function showStartMenu() { showEntity(menuContainer); hideEntity(menuGameOver); showEntity(menuStart); startButton.classList.add('clickable'); restartButton.classList.remove('clickable'); }

تحقق جيدًا من تطابق التعليمات البرمجية الخاصة بك مع ملفات المصدر المقابلة. بعد ذلك ، انتقل إلى المعاينة الخاصة بك ، وستلاحظ السلوك التالي:

ابدأ واللعب على القوائم
قوائم Start و Game Over (معاينة كبيرة)

هذا يختتم قائمتي Start و Game Over .

تهانينا! لديك الآن لعبة تعمل بكامل طاقتها وبداية مناسبة ونهاية مناسبة. ومع ذلك ، يتبقى لدينا خطوة واحدة في هذا البرنامج التعليمي: نحن بحاجة إلى مزامنة حالة اللعبة بين أجهزة اللاعبين المختلفة. سيؤدي هذا إلى تقريبنا خطوة واحدة نحو الألعاب متعددة اللاعبين.

الخطوة 3: مزامنة حالة اللعبة مع MirrorVR

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

ملاحظة : يمكنك معرفة المزيد عن MirrorVR هنا.

انتقل إلى index.html . هنا ، سنقوم بتحميل MirrorVR وإضافة مكون إلى الكاميرا ، مما يشير إلى أنه يجب أن يعكس عرض الجهاز المحمول عند الاقتضاء. استيراد التبعية socket.io و MirrorVR 0.2.3.

 <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.1.1/socket.io.js"></script> <script src="https://cdn.jsdelivr.net/gh/alvinwan/[email protected]/dist/mirrorvr.min.js"></script>

بعد ذلك ، أضف مكونًا ، camera-listener ، إلى الكاميرا:

 <a-camera camera-listener ...>

انتقل إلى الأصول / ergo.js. في هذه الخطوة ، سيرسل الجهاز المحمول أوامر ، وسيعكس جهاز سطح المكتب الجهاز المحمول فقط.

لتسهيل ذلك ، تحتاج إلى أداة مساعدة للتمييز بين أجهزة سطح المكتب والأجهزة المحمولة. في نهاية الملف ، أضف وظيفة mobileCheck بعد shuffle :

 /** * Checks for mobile and tablet platforms. */ function mobileCheck() { var check = false; (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[aw])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera); return check; };

أولاً ، سنقوم بمزامنة بدء اللعبة. في بداية اللعبة ، من قسم اللعبة ، أضف mirrorVR startGame النهاية.

 function startGame() { ... if (mobileCheck()) { mirrorVR.notify('startGame', {}) } }

يرسل عميل الهاتف المحمول الآن إشعارات حول بدء اللعبة. ستقوم الآن بتنفيذ استجابة سطح المكتب.

في مستمع تحميل النافذة ، قم باستدعاء وظيفة setupMirrorVR :

 window.onload = function() { ... setupMirrorVR(); }

حدد قسمًا جديدًا أعلى قسم Game لإعداد MirrorVR:

 /************ * MirrorVR * ************/ function setupMirrorVR() { mirrorVR.init(); }

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

 function setupMirrorVR() { mirrorVR.init({ roomId: 'ergo', state: { startGame: { onNotify: function(data) { hideAllMenus(); setupScore(); updateScoreDisplay(); } }, } }); }

كرر نفس عملية المزامنة مع Game Over . في gameOver في قسم Game ، أضف فحصًا للأجهزة المحمولة وأرسل إشعارًا وفقًا لذلك:

 function gameOver() { ... if (mobileCheck()) { mirrorVR.notify('gameOver', {}); } }

انتقل إلى قسم MirrorVR وقم بتحديث وسيطات الكلمات الأساسية باستخدام مستمع gameOver :

 function setupMirrorVR() { mirrorVR.init({ state: { startGame: {... }, gameOver: { onNotify: function(data) { gameOver(); } }, } }) }

بعد ذلك ، كرر نفس عملية المزامنة لإضافة الأشجار. انتقل إلى إضافة الأشجار addTreesRandomly في قسم Trees . تتبع الممرات التي تتلقى أشجارًا جديدة. بعد ذلك ، مباشرة قبل توجيه return ، وأرسل إشعارًا وفقًا لذلك:

 function addTreesRandomly(...) { ... var numberOfTreesAdded ... var position_indices = []; trees.forEach(function (tree) { if (...) { ... position_indices.push(tree.position_index); } }); if (mobileCheck()) { mirrorVR.notify('addTrees', position_indices); } return ... }

انتقل إلى قسم MirrorVR ، وقم بتحديث وسيطات الكلمات الأساسية إلى mirrorVR.init باستخدام مستمع جديد للأشجار:

 function setupMirrorVR() { mirrorVR.init({ state: { ... gameOver: {... }, addTrees: { onNotify: function(position_indices) { position_indices.forEach(addTreeTo) } }, } }) }

أخيرًا ، نقوم بمزامنة نتيجة اللعبة. في updateScoreDisplay من قسم Score ، أرسل إشعارًا عند الاقتضاء:

 function updateScoreDisplay() { ... if (mobileCheck()) { mirrorVR.notify('score', score); } }

قم بتحديث تهيئة mirrorVR للمرة الأخيرة ، مع مستمع لتغييرات النتيجة:

 function setupMirrorVR() { mirrorVR.init({ state: { addTrees: { }, score: { onNotify: function(data) { score = data; updateScoreDisplay(); } } } }); }

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

هنا عرض. لاحظ أن مؤشر سطح المكتب لا يتحرك ، مما يشير إلى أن الجهاز المحمول يتحكم في معاينة سطح المكتب.

لعبة عداء نهائي لا نهاية لها مع مزامنة حالة لعبة MirrorVR
النتيجة النهائية للعبة الجري التي لا نهاية لها مع مزامنة حالة لعبة MirrorVR (معاينة كبيرة)

هذا يختتم مشروعك المعزز بـ MirrorVR.

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

خاتمة

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

يمكن أن تشمل الخطوات التالية:

  • المزيد من النمذجة المتقدمة
    وهذا يعني نماذج ثلاثية الأبعاد أكثر واقعية ، يُحتمل إنشاؤها في برنامج جهة خارجية ومستوردة. على سبيل المثال ، (MagicaVoxel) يجعل إنشاء فن voxel بسيطًا ، و (Blender) هو حل كامل للنمذجة ثلاثية الأبعاد.
  • المزيد من التعقيد
    يمكن للألعاب الأكثر تعقيدًا ، مثل الألعاب الإستراتيجية في الوقت الفعلي ، الاستفادة من محرك طرف ثالث لزيادة الكفاءة. قد يعني هذا تجنب A-Frame و webVR تمامًا ، بدلاً من نشر لعبة مجمعة (Unity3d).

تشمل السبل الأخرى دعمًا متعدد اللاعبين ورسومات أكثر ثراءً. مع اختتام هذه السلسلة التعليمية ، لديك الآن إطار عمل لاستكشافه بشكل أكبر.