วิธีสร้างเกมวิ่งที่ไม่มีที่สิ้นสุดในความเป็นจริงเสมือน (ตอนที่ 3)
เผยแพร่แล้ว: 2022-03-10และการเดินทางของเรายังคงดำเนินต่อไป ในส่วนสุดท้ายของซีรีส์นี้เกี่ยวกับวิธีสร้างเกม 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 เดียวกันบนอุปกรณ์มือถือของคุณ ทันทีที่อุปกรณ์มือถือของคุณโหลดหน้าเว็บ เดสก์ท็อปของคุณควรเริ่มจำลองเกมของอุปกรณ์มือถือทันที
นี่คือการสาธิต โปรดสังเกตว่าเคอร์เซอร์เดสก์ท็อปไม่เคลื่อนที่ แสดงว่าอุปกรณ์เคลื่อนที่กำลังควบคุมการแสดงตัวอย่างเดสก์ท็อป
นี่เป็นการสรุปโครงการเสริมของคุณด้วย mirrorVR
ขั้นตอนที่สามนี้แนะนำขั้นตอนการซิงโครไนซ์สถานะเกมพื้นฐานสองสามขั้นตอน เพื่อให้มีประสิทธิภาพมากขึ้น คุณสามารถเพิ่มการตรวจสุขภาพจิตและจุดซิงค์เพิ่มเติมได้
บทสรุป
ในบทช่วยสอนนี้ คุณได้เพิ่มการตกแต่งขั้นสุดท้ายให้กับเกมวิ่งไม่รู้จบของคุณ และใช้การซิงโครไนซ์ไคลเอนต์เดสก์ท็อปกับไคลเอนต์มือถือแบบเรียลไทม์ ซึ่งสะท้อนหน้าจอของอุปกรณ์มือถือบนเดสก์ท็อปของคุณอย่างมีประสิทธิภาพ นี่เป็นการสรุปซีรีส์เรื่องการสร้างเกมวิ่งที่ไม่มีที่สิ้นสุดในความเป็นจริงเสมือน นอกจากเทคนิค A-Frame VR แล้ว คุณยังเลือกการสร้างแบบจำลอง 3 มิติ การสื่อสารระหว่างไคลเอ็นต์กับไคลเอ็นต์ และแนวคิดอื่นๆ ที่เกี่ยวข้องอย่างกว้างขวาง
ขั้นตอนถัดไปอาจรวมถึง:
- การสร้างแบบจำลองขั้นสูงเพิ่มเติม
นี่หมายถึงโมเดล 3 มิติที่สมจริงยิ่งขึ้น ซึ่งอาจสร้างขึ้นในซอฟต์แวร์ของบุคคลที่สามและนำเข้า ตัวอย่างเช่น (MagicaVoxel) ทำให้การสร้าง voxel art เป็นเรื่องง่าย และ (Blender) เป็นโซลูชันการสร้างแบบจำลอง 3 มิติที่สมบูรณ์ - ความซับซ้อนมากขึ้น
เกมที่ซับซ้อนมากขึ้น เช่น เกมวางแผนแบบเรียลไทม์ สามารถใช้ประโยชน์จากเอ็นจิ้นของบริษัทอื่นเพื่อประสิทธิภาพที่เพิ่มขึ้น ซึ่งอาจหมายถึงการเลี่ยง A-Frame และ webVR โดยสิ้นเชิง แทนที่จะเผยแพร่เกมที่คอมไพล์แล้ว (Unity3d)
ช่องทางอื่นๆ ได้แก่ การสนับสนุนผู้เล่นหลายคนและกราฟิกที่สมบูรณ์ยิ่งขึ้น ด้วยบทสรุปของชุดการสอนนี้ ตอนนี้คุณมีกรอบงานในการสำรวจเพิ่มเติม