วิธีสร้างเกมวิ่งที่ไม่มีที่สิ้นสุดในความเป็นจริงเสมือน (ตอนที่ 3)

เผยแพร่แล้ว: 2022-03-10
สรุปโดยย่อ ↬ ในตอนที่ 1 Alvin ได้อธิบายพื้นฐานของวิธีออกแบบแบบจำลองความเป็นจริงเสมือน ในตอนที่ 2 เขาแสดงวิธีการใช้ตรรกะหลักของเกม ในส่วนสุดท้ายของบทช่วยสอนนี้ จะมีการเพิ่มการตกแต่งขั้นสุดท้าย เช่น เมนู "เริ่ม" และ "จบเกม" รวมถึงการซิงโครไนซ์สถานะเกมระหว่างไคลเอนต์มือถือและเดสก์ท็อป นี่เป็นการปูทางสำหรับแนวคิดในการสร้างเกมที่มีผู้เล่นหลายคน

และการเดินทางของเรายังคงดำเนินต่อไป ในส่วนสุดท้ายของซีรีส์นี้เกี่ยวกับวิธีสร้างเกม VR แบบไม่มีที่สิ้นสุด ฉันจะแสดงให้คุณเห็นว่าคุณสามารถซิงโครไนซ์สถานะเกมระหว่างอุปกรณ์สองเครื่องได้อย่างไร ซึ่งจะทำให้คุณเข้าใกล้การสร้างเกมแบบผู้เล่นหลายคนมากขึ้นอีกก้าวหนึ่ง ฉันจะแนะนำ MirrorVR โดยเฉพาะซึ่งรับผิดชอบในการจัดการเซิร์ฟเวอร์สื่อกลางในการสื่อสารระหว่างไคลเอ็นต์กับไคลเอ็นต์

หมายเหตุ : เกมนี้สามารถเล่นได้โดยมีหรือไม่มีชุดหูฟัง VR คุณสามารถดูการสาธิตผลิตภัณฑ์ขั้นสุดท้ายได้ที่ ergo-3.glitch.me

ในการเริ่มต้น คุณจะต้องมีสิ่งต่อไปนี้

  • การเข้าถึงอินเทอร์เน็ต (โดยเฉพาะกับ glitch.com);
  • โครงการ Glitch เสร็จสมบูรณ์จากส่วนที่ 2 ของบทช่วยสอนนี้ คุณสามารถเริ่มจากส่วนที่ 2 ของผลิตภัณฑ์ที่ทำเสร็จแล้วโดยไปที่ https://glitch.com/edit/#!/ergo-2 และคลิก “Remix to edit”;
  • ชุดหูฟังเสมือนจริง (แนะนำเสริม) (ฉันใช้ 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 นำทางไปยัง asset/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(); ... }

ตรวจสอบให้แน่ใจว่า asset/ergo.js และ index.html ตรงกับไฟล์ซอร์สโค้ดที่เกี่ยวข้อง จากนั้นไปที่หน้าตัวอย่างของคุณ คุณควรเห็นสิ่งต่อไปนี้:

การแสดงคะแนน
การแสดงคะแนน (ตัวอย่างขนาดใหญ่)

นี้สรุปการแสดงคะแนน ต่อไป เราจะเพิ่มเมนูเริ่มและ เกมโอเวอร์ ที่เหมาะสม เพื่อให้ผู้เล่นสามารถเล่นเกมซ้ำได้ตามต้องการ

ขั้นตอนที่ 2: เพิ่มเมนูเริ่ม

ตอนนี้ผู้ใช้สามารถติดตามความคืบหน้าได้ คุณจะเพิ่มการตกแต่งขั้นสุดท้ายเพื่อให้ประสบการณ์การเล่นเกมสมบูรณ์ ในขั้นตอนนี้ คุณจะต้องเพิ่มเมนู Start และเมนู Game Over เพื่อให้ผู้ใช้เริ่มและรีสตาร์ทเกม

เริ่มต้นด้วยเมนู เริ่ม ที่ผู้เล่น คลิก ปุ่ม "เริ่ม" เพื่อเริ่มเกม ในช่วงครึ่งหลังของขั้นตอนนี้ คุณจะเพิ่มเมนู Game Over ด้วยปุ่ม "Restart":

เริ่มและเล่นเกมผ่านเมนู
เมนูเริ่มและเกมโอเวอร์ (ตัวอย่างขนาดใหญ่)

ไปที่ 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>

ในการทำให้เมนู Start สมบูรณ์ ให้เพิ่มปุ่มที่เขียนว่า “Start”:

 <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 ของเมนู Start ตรงกับรายการต่อไปนี้:

 <!-- 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>

ไปที่หน้าตัวอย่าง แล้วคุณจะเห็นเมนู Start ต่อไปนี้:

รูปภาพของเมนูเริ่ม
เมนูเริ่ม (ตัวอย่างขนาดใหญ่)

ยังอยู่ในส่วน 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 ของคุณ asset/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 ให้ลบการเรียกใช้โดยตรงเพื่อ startGame และเรียก setupAllMenus แทน อัปเดตผู้ฟังของคุณให้ตรงกับสิ่งต่อไปนี้:

 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'); }

ถัดไป ผูกทั้งปุ่ม "Start" และ "Restart" เพื่อ 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'); }

ตรวจสอบอีกครั้งว่าโค้ดของคุณตรงกับซอร์สไฟล์ที่เกี่ยวข้อง จากนั้น ไปที่หน้าตัวอย่างของคุณ และคุณจะสังเกตพฤติกรรมต่อไปนี้:

เริ่มและเล่นเกมผ่านเมนู
เมนูเริ่มและเกมโอเวอร์ (ตัวอย่างขนาดใหญ่)

นี่เป็นการสรุปเมนู เริ่ม และ เกมโอเวอร์

ยินดีด้วย! ตอนนี้คุณมีเกมที่ทำงานได้อย่างสมบูรณ์พร้อมจุดเริ่มต้นและจุดสิ้นสุดที่เหมาะสม อย่างไรก็ตาม เราเหลืออีกหนึ่งขั้นตอนในบทช่วยสอนนี้: เราจำเป็นต้องซิงโครไนซ์สถานะเกมระหว่างอุปกรณ์ของผู้เล่นที่แตกต่างกัน สิ่งนี้จะทำให้เราเข้าใกล้เกมที่มีผู้เล่นหลายคนมากขึ้นไปอีกก้าวหนึ่ง

ขั้นตอนที่ 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 ...>

นำทางไปยัง asset/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; };

อันดับแรก เราจะซิงโครไนซ์การเริ่มเกม ใน startGame ของส่วน Game ให้เพิ่มการแจ้งเตือน mirrorVR ในตอนท้าย

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

ตอนนี้ไคลเอนต์มือถือส่งการแจ้งเตือนเกี่ยวกับการเริ่มเกม ตอนนี้คุณจะใช้การตอบสนองของเดสก์ท็อป

ใน window load listener ให้เรียกใช้ฟังก์ชัน 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 ด้วย listener ใหม่สำหรับทรี:

 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 เดียวกันบนอุปกรณ์มือถือของคุณ ทันทีที่อุปกรณ์มือถือของคุณโหลดหน้าเว็บ เดสก์ท็อปของคุณควรเริ่มจำลองเกมของอุปกรณ์มือถือทันที

นี่คือการสาธิต โปรดสังเกตว่าเคอร์เซอร์เดสก์ท็อปไม่เคลื่อนที่ แสดงว่าอุปกรณ์เคลื่อนที่กำลังควบคุมการแสดงตัวอย่างเดสก์ท็อป

เกม Endless Runner สุดท้ายพร้อมการซิงโครไนซ์สถานะเกม MirrorVR
ผลลัพธ์สุดท้ายของเกมวิ่งไม่มีที่สิ้นสุดพร้อมการซิงโครไนซ์สถานะเกม MirrorVR (ตัวอย่างขนาดใหญ่)

นี่เป็นการสรุปโครงการเสริมของคุณด้วย mirrorVR

ขั้นตอนที่สามนี้แนะนำขั้นตอนการซิงโครไนซ์สถานะเกมพื้นฐานสองสามขั้นตอน เพื่อให้มีประสิทธิภาพมากขึ้น คุณสามารถเพิ่มการตรวจสุขภาพจิตและจุดซิงค์เพิ่มเติมได้

บทสรุป

ในบทช่วยสอนนี้ คุณได้เพิ่มการตกแต่งขั้นสุดท้ายให้กับเกมวิ่งไม่รู้จบของคุณ และใช้การซิงโครไนซ์ไคลเอนต์เดสก์ท็อปกับไคลเอนต์มือถือแบบเรียลไทม์ ซึ่งสะท้อนหน้าจอของอุปกรณ์มือถือบนเดสก์ท็อปของคุณอย่างมีประสิทธิภาพ นี่เป็นการสรุปซีรีส์เรื่องการสร้างเกมวิ่งที่ไม่มีที่สิ้นสุดในความเป็นจริงเสมือน นอกจากเทคนิค A-Frame VR แล้ว คุณยังเลือกการสร้างแบบจำลอง 3 มิติ การสื่อสารระหว่างไคลเอ็นต์กับไคลเอ็นต์ และแนวคิดอื่นๆ ที่เกี่ยวข้องอย่างกว้างขวาง

ขั้นตอนถัดไปอาจรวมถึง:

  • การสร้างแบบจำลองขั้นสูงเพิ่มเติม
    นี่หมายถึงโมเดล 3 มิติที่สมจริงยิ่งขึ้น ซึ่งอาจสร้างขึ้นในซอฟต์แวร์ของบุคคลที่สามและนำเข้า ตัวอย่างเช่น (MagicaVoxel) ทำให้การสร้าง voxel art เป็นเรื่องง่าย และ (Blender) เป็นโซลูชันการสร้างแบบจำลอง 3 มิติที่สมบูรณ์
  • ความซับซ้อนมากขึ้น
    เกมที่ซับซ้อนมากขึ้น เช่น เกมวางแผนแบบเรียลไทม์ สามารถใช้ประโยชน์จากเอ็นจิ้นของบริษัทอื่นเพื่อประสิทธิภาพที่เพิ่มขึ้น ซึ่งอาจหมายถึงการเลี่ยง A-Frame และ webVR โดยสิ้นเชิง แทนที่จะเผยแพร่เกมที่คอมไพล์แล้ว (Unity3d)

ช่องทางอื่นๆ ได้แก่ การสนับสนุนผู้เล่นหลายคนและกราฟิกที่สมบูรณ์ยิ่งขึ้น ด้วยบทสรุปของชุดการสอนนี้ ตอนนี้คุณมีกรอบงานในการสำรวจเพิ่มเติม