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

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

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

บทช่วยสอนนี้เกี่ยวข้องกับหลายขั้นตอน ซึ่งรวมถึง (แต่ไม่จำกัดเพียง) การตรวจจับการชนกัน และแนวคิด A-Frame อื่นๆ เช่น มิกซ์อิน

  • สาธิตผลิตภัณฑ์ขั้นสุดท้าย

ข้อกำหนดเบื้องต้น

เช่นเดียวกับในบทช่วยสอนก่อนหน้านี้ คุณจะต้องมีสิ่งต่อไปนี้:

  • การเข้าถึงอินเทอร์เน็ต (โดยเฉพาะกับ glitch.com);
  • โครงการ Glitch ที่เสร็จสิ้นจากส่วนที่ 1 (คุณสามารถดำเนินการต่อจากผลิตภัณฑ์ที่ทำเสร็จแล้วได้โดยไปที่ https://glitch.com/edit/#!/ergo-1 และคลิก “Remix to edit”;
  • ชุดหูฟังเสมือนจริง (แนะนำเสริม) (ฉันใช้ Google Cardboard ซึ่งมีราคา 15 เหรียญต่อชิ้น)
เพิ่มเติมหลังกระโดด! อ่านต่อด้านล่าง↓

ขั้นตอนที่ 1: การออกแบบอุปสรรค

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

ต้นไม้แม่แบบเคลื่อนเข้าหาผู้เล่น
ต้นไม้เทมเพลตเคลื่อนเข้าหาผู้เล่น (ตัวอย่างขนาดใหญ่)

ต้นไม้เหล่านี้จะทำหน้าที่เป็นแม่แบบสำหรับอุปสรรคที่คุณสร้างระหว่างเกม สำหรับส่วนสุดท้ายของขั้นตอนนี้ เราจะลบ "แผนผังต้นไม้" เหล่านี้

ในการเริ่มต้น ให้เพิ่ม A-Frame mixins ต่างๆ จำนวนหนึ่ง มิกซ์อินคือชุดคุณสมบัติของส่วนประกอบที่ใช้กันทั่วไป ในกรณีของเรา ต้นไม้ทั้งหมดของเราจะมีสี ความสูง ความกว้าง ความลึก ฯลฯ เหมือนกัน กล่าวอีกนัยหนึ่ง ต้นไม้ทั้งหมดของคุณจะมีลักษณะเหมือนกัน ดังนั้นจะใช้มิกซ์อินร่วมกันสองสามอย่าง

หมายเหตุ : ในบทช่วยสอนของเรา เนื้อหาเดียวของคุณจะเป็นมิกซ์อิน ไปที่หน้า A-Frame Mixins เพื่อเรียนรู้เพิ่มเติม

ในโปรแกรมแก้ไขของคุณ ให้ไปที่ index.html ทันทีหลังท้องฟ้าและก่อนแสงของคุณ เพิ่มเอนทิตี A-Frame ใหม่เพื่อถือทรัพย์สินของคุณ:

 <a-sky...></a-sky> <!-- Mixins --> <a-assets> </a-assets> <!-- Lights --> ...

ในเอนทิตี a-assets ใหม่ของคุณ ให้เริ่มต้นด้วยการเพิ่มมิกซ์อินสำหรับใบไม้ของคุณ มิกซ์อินนี้กำหนดคุณสมบัติทั่วไปสำหรับใบไม้ของต้นไม้เทมเพลต กล่าวโดยสรุป มันคือปิรามิดสีขาวแรเงาแบนสำหรับเอฟเฟกต์โพลีต่ำ

 <a-assets> <a-mixin geometry=" primitive: cone; segments-height: 1; segments-radial:4; radius-bottom:0.3;" material="color:white;flat-shading: true;"></a-mixin> </a-assets>

ด้านล่างมิกซ์อินใบไม้ของคุณ ให้เพิ่มมิกซ์อินสำหรับลำต้น ลำต้นนี้จะเป็นปริซึมสี่เหลี่ยมเล็กๆ สีขาว

 <a-assets> ... <a-mixin geometry=" primitive: box; height:0.5; width:0.1; depth:0.1;" material="color:white;"></a-mixin> </a-assets>

ถัดไป เพิ่มออบเจกต์ทรีเทมเพลตที่จะใช้มิกซ์อินเหล่านี้ ยังอยู่ใน index.html ให้เลื่อนลงไปที่ส่วนแพลตฟอร์ม ก่อนส่วนผู้เล่น เพิ่มส่วนต้นไม้ใหม่ โดยมีต้นไม้ว่างสามรายการ:

 <a-entity ...> <!-- Trees --> <a-entity></a-entity> <a-entity></a-entity> <a-entity></a-entity> <!-- Player --> ...

ถัดไป ปรับตำแหน่ง ปรับขนาด และเพิ่มเงาให้กับเอนทิตีของต้นไม้

 <!-- Trees --> <a-entity shadow scale="0.3 0.3 0.3" position="0 0.6 0"></a-entity> <a-entity shadow scale="0.3 0.3 0.3" position="0 0.6 0"></a-entity> <a-entity shadow scale="0.3 0.3 0.3" position="0 0.6 0"></a-entity>

ตอนนี้ เติมเอนทิตีของต้นไม้ด้วยลำต้นและใบไม้ โดยใช้มิกซ์อินที่เรากำหนดไว้ก่อนหน้านี้

 <!-- Trees --> <a-entity ...> <a-entity mixin="foliage"></a-entity> <a-entity mixin="trunk" position="0 -0.5 0"></a-entity> </a-entity> <a-entity ...> <a-entity mixin="foliage"></a-entity> <a-entity mixin="trunk" position="0 -0.5 0"></a-entity> </a-entity> <a-entity ...> <a-entity mixin="foliage"></a-entity> <a-entity mixin="trunk" position="0 -0.5 0"></a-entity> </a-entity>

ไปที่การแสดงตัวอย่างของคุณ และตอนนี้คุณควรเห็นแผนผังแม่แบบต่อไปนี้

ต้นไม้ต้นแบบสำหรับอุปสรรค
ต้นไม้เทมเพลตสำหรับอุปสรรค (ตัวอย่างขนาดใหญ่)

ตอนนี้ ทำให้ต้นไม้เคลื่อนไหวจากตำแหน่งที่ห่างไกลบนแพลตฟอร์มไปยังผู้ใช้ เช่นเคย ใช้แท็ก a-animation :

 <!-- Trees --> <a-entity ...> ... <a-animation attribute="position" ease="linear" from="0 0.6 -7" to="0 0.6 1.5" dur="5000"></a-animation> </a-entity> <a-entity ...> ... <a-animation attribute="position" ease="linear" from="-0.5 0.55 -7" to="-0.5 0.55 1.5" dur="5000"></a-animation> </a-entity> <a-entity ...> ... <a-animation attribute="position" ease="linear" from="0.5 0.55 -7" to="0.5 0.55 1.5" dur="5000"></a-animation> </a-entity>

ตรวจสอบให้แน่ใจว่ารหัสของคุณตรงกับรายการต่อไปนี้

 <a-entity...> <!-- Trees --> <a-entity shadow scale="0.3 0.3 0.3" position="0 0.6 0"> <a-entity mixin="foliage"></a-entity> <a-entity mixin="trunk" position="0 -0.5 0"></a-entity> <a-animation attribute="position" ease="linear" from="0 0.6 -7" to="0 0.6 1.5" dur="5000"></a-animation> </a-entity> <a-entity shadow scale="0.3 0.3 0.3" position="-0.5 0.55 0"> <a-entity mixin="foliage"></a-entity> <a-entity mixin="trunk" position="0 -0.5 0"></a-entity> <a-animation attribute="position" ease="linear" from="-0.5 0.55 -7" to="-0.5 0.55 1.5" dur="5000"></a-animation> </a-entity> <a-entity shadow scale="0.3 0.3 0.3" position="0.5 0.55 0"> <a-entity mixin="foliage"></a-entity> <a-entity mixin="trunk" position="0 -0.5 0"></a-entity> <a-animation attribute="position" ease="linear" from="0.5 0.55 -7" to="0.5 0.55 1.5" dur="5000"></a-animation> </a-entity> <!-- Player --> ...

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

ต้นไม้แม่แบบเคลื่อนเข้าหาผู้เล่น
ต้นไม้แม่แบบเคลื่อนเข้าหาผู้เล่น ต้นไม้แม่แบบเคลื่อนเข้าหาผู้เล่น (ตัวอย่างขนาดใหญ่)

กลับไปที่โปรแกรมแก้ไขของคุณ คราวนี้ เลือก asset/ergo.js ในส่วนเกม ให้ตั้งค่าต้นไม้หลังจากโหลดหน้าต่างแล้ว

 /******** * GAME * ********/ ... window.onload = function() { setupTrees(); }

ใต้ส่วนควบคุม แต่ก่อนส่วนเกม ให้เพิ่มส่วน TREES ใหม่ ในส่วนนี้ ให้กำหนดฟังก์ชัน setupTrees ใหม่

 /************ * CONTROLS * ************/ ... /********* * TREES * *********/ function setupTrees() { } /******** * GAME * ********/ ...

ในฟังก์ชัน setupTrees ใหม่ รับการอ้างอิงไปยังออบเจ็กต์ DOM ของแผนผังเทมเพลต และทำให้ข้อมูลอ้างอิงพร้อมใช้งานทั่วโลก

 /********* * TREES * *********/ var templateTreeLeft; var templateTreeCenter; var templateTreeRight; function setupTrees() { templateTreeLeft = document.getElementById('template-tree-left'); templateTreeCenter = document.getElementById('template-tree-center'); templateTreeRight = document.getElementById('template-tree-right'); }

ถัดไป กำหนดยูทิลิตี้ removeTree ใหม่ ด้วยยูทิลิตี้นี้ คุณสามารถลบแผนผังแม่แบบออกจากฉากได้ ภายใต้ฟังก์ชัน setupTrees ให้กำหนดยูทิลิตี้ใหม่ของคุณ

 function setupTrees() { ... } function removeTree(tree) { tree.parentNode.removeChild(tree); }

กลับไปที่ setupTrees ใช้ยูทิลิตี้ใหม่เพื่อลบแผนผังแม่แบบ

 function setupTrees() { ... removeTree(templateTreeLeft); removeTree(templateTreeRight); removeTree(templateTreeCenter); }

ตรวจสอบให้แน่ใจว่าส่วนต้นไม้และเกมของคุณตรงกับสิ่งต่อไปนี้:

 /********* * TREES * *********/ var templateTreeLeft; var templateTreeCenter; var templateTreeRight; function setupTrees() { templateTreeLeft = document.getElementById('template-tree-left'); templateTreeCenter = document.getElementById('template-tree-center'); templateTreeRight = document.getElementById('template-tree-right'); removeTree(templateTreeLeft); removeTree(templateTreeRight); removeTree(templateTreeCenter); } function removeTree(tree) { tree.parentNode.removeChild(tree); } /******** * GAME * ********/ setupControls(); // TODO: AFRAME.registerComponent has to occur before window.onload? window.onload = function() { setupTrees(); }

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

ส่วนที่ 1 ผลิตภัณฑ์สำเร็จรูป
ส่วนที่ 1 ผลิตภัณฑ์สำเร็จรูป (ตัวอย่างขนาดใหญ่)

นี่เป็นการสรุปการออกแบบแผนผังเทมเพลต

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

ในขั้นตอนต่อไป เราจะวางไข่สิ่งกีดขวางมากมายและออกแบบอัลกอริธึมง่ายๆ เพื่อกระจายต้นไม้ไปตามเลนต่างๆ

ขั้นตอนที่ 2: อุปสรรคการวางไข่

ในเกมวิ่งที่ไม่มีที่สิ้นสุด เป้าหมายของเราคือหลีกเลี่ยงอุปสรรคที่พุ่งเข้ามาหาเรา ในการใช้งานเกมนี้โดยเฉพาะ เราใช้สามเลนตามปกติ

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

ในขั้นตอนนี้ การแก้ไขโค้ดทั้งหมดของเราจะทำใน asset/ergo.js ไฟล์ HTML จะยังคงเหมือนเดิม ไปที่ส่วน TREES ของ asset/ergo.js

ในการเริ่มต้น เราจะเพิ่มยูทิลิตี้เพื่อวางไข่ต้นไม้ ต้นไม้ทุกต้นจะต้องมี ID เฉพาะ ซึ่งเราจะกำหนดจำนวนต้นไม้ที่มีอยู่เมื่อต้นไม้เกิด เริ่มต้นด้วยการติดตามจำนวนต้นไม้ในตัวแปรส่วนกลาง

 /********* * TREES * *********/ ... var numberOfTrees = 0; function setupTrees() { ...

ต่อไป เราจะเริ่มต้นการอ้างอิงถึงองค์ประกอบ DOM ของคอนเทนเนอร์ทรี ซึ่งฟังก์ชัน spawn ของเราจะเพิ่มต้นไม้เข้าไป ยังอยู่ในส่วน TREES ให้เพิ่มตัวแปรร่วมแล้วทำการอ้างอิง

 ... var treeContainer; var numberOfTrees ... function setupTrees() { ... templateTreeRight = ... treeContainer = document.getElementById('tree-container'); removeTree(...); ... }

ใช้ทั้งจำนวนต้นไม้และที่เก็บต้นไม้ เขียนฟังก์ชันใหม่ที่จะวางไข่ต้นไม้

 function removeTree(tree) { ... } function addTree(el) { numberOfTrees += 1; el.id = 'tree-' + numberOfTrees; treeContainer.appendChild(el); } ...

เพื่อความสะดวกในการใช้งานในภายหลัง คุณจะต้องสร้างฟังก์ชันที่สองที่เพิ่มต้นไม้ที่ถูกต้องลงในเลนที่ถูกต้อง ในการเริ่มต้น ให้กำหนดอาร์เรย์ templates ใหม่ในส่วน TREES

 var templates; var treeContainer; ... function setupTrees() { ... templates = [templateTreeLeft, templateTreeCenter, templateTreeRight]; removeTree(...); ... }

ใช้อาร์เรย์เทมเพลตนี้ เพิ่มยูทิลิตี้ที่สร้างต้นไม้ในเลนที่กำหนด โดยให้ ID แทนซ้าย กลาง หรือขวา

 function function addTree(el) { ... } function addTreeTo(position_index) { var template = templates[position_index]; addTree(template.cloneNode(true)); }

ไปที่หน้าตัวอย่างและเปิดคอนโซลนักพัฒนาซอฟต์แวร์ของคุณ ในคอนโซลนักพัฒนาซอฟต์แวร์ของคุณ ให้เรียกใช้ฟังก์ชัน addTreeTo

 > addTreeTo(0); # spawns tree in left lane 
เรียกใช้ addTreeTo ด้วยตนเอง
เรียก addTreeTo ด้วยตนเอง (ตัวอย่างขนาดใหญ่)

ตอนนี้ คุณจะเขียนอัลกอริทึมที่วางไข่ต้นไม้แบบสุ่ม:

  1. เลือกเลนแบบสุ่ม (ที่ยังไม่ได้เลือกสำหรับขั้นตอนนี้);
  2. วางไข่ต้นไม้ด้วยความน่าจะเป็น;
  3. หากเกิดจำนวนต้นไม้สูงสุดในช่วงเวลานี้ ให้หยุด มิฉะนั้น ทำซ้ำขั้นตอนที่ 1

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

 function addTreeTo(position_index) { ... } /** * Add any number of trees across different lanes, randomly. **/ function addTreesRandomly( { probTreeLeft = 0.5, probTreeCenter = 0.5, probTreeRight = 0.5, maxNumberTrees = 2 } = {}) { }

ในฟังก์ชัน addTreesRandomly ใหม่ของคุณ ให้กำหนดรายการแผนผังเทมเพลต และสับเปลี่ยนรายการ

 function addTreesRandomly( ... ) { var trees = [ {probability: probTreeLeft, position_index: 0}, {probability: probTreeCenter, position_index: 1}, {probability: probTreeRight, position_index: 2}, ] shuffle(trees); }

เลื่อนลงไปที่ด้านล่างของไฟล์ และสร้างส่วนยูทิลิตี้ใหม่ พร้อมกับยูทิลิตี้ shuffle ใหม่ ยูทิลิตีนี้จะสับเปลี่ยนอาร์เรย์ให้เข้าที่

 /******** * GAME * ********/ ... /************* * UTILITIES * *************/ /** * Shuffles array in place. * @param {Array} a items An array containing the items. */ function shuffle(a) { var j, x, i; for (i = a.length - 1; i > 0; i--) { j = Math.floor(Math.random() * (i + 1)); x = a[i]; a[i] = a[j]; a[j] = x; } return a; }

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

 function addTreesRandomly( ... ) { ... var numberOfTreesAdded = 0; trees.forEach(function (tree) { }); }

ในการวนซ้ำบนต้นไม้ วางไข่ต้นไม้ด้วยความน่าจะเป็นบางอย่างเท่านั้น และเฉพาะในกรณีที่จำนวนต้นไม้ที่เพิ่มไม่เกิน 2 . อัพเดท for loop ดังนี้

 function addTreesRandomly( ... ) { ... trees.forEach(function (tree) { if (Math.random() < tree.probability && numberOfTreesAdded < maxNumberTrees) { addTreeTo(tree.position_index); numberOfTreesAdded += 1; } }); }

ในการสรุปฟังก์ชัน ให้คืนค่าจำนวนต้นไม้ที่เพิ่ม

 function addTreesRandomly( ... ) { ... return numberOfTreesAdded; }

ตรวจสอบอีกครั้งว่าฟังก์ชัน addTreesRandomly ของคุณตรงกับรายการต่อไปนี้

 /** * Add any number of trees across different lanes, randomly. **/ function addTreesRandomly( { probTreeLeft = 0.5, probTreeCenter = 0.5, probTreeRight = 0.5, maxNumberTrees = 2 } = {}) { var trees = [ {probability: probTreeLeft, position_index: 0}, {probability: probTreeCenter, position_index: 1}, {probability: probTreeRight, position_index: 2}, ] shuffle(trees); var numberOfTreesAdded = 0; trees.forEach(function (tree) { if (Math.random() < tree.probability && numberOfTreesAdded < maxNumberTrees) { addTreeTo(tree.position_index); numberOfTreesAdded += 1; } }); return numberOfTreesAdded; }

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

 /********* * TREES * *********/ ... var treeTimer; function setupTrees() { ... } function teardownTrees() { clearInterval(treeTimer); }

ถัดไป กำหนดฟังก์ชันใหม่ที่เริ่มต้นตัวจับเวลาและบันทึกตัวจับเวลาในตัวแปรส่วนกลางที่กำหนดไว้ก่อนหน้านี้ ตัวจับเวลาด้านล่างจะทำงานทุกครึ่งวินาที

 function addTreesRandomlyLoop({intervalLength = 500} = {}) { treeTimer = setInterval(addTreesRandomly, intervalLength); }

สุดท้าย เริ่มจับเวลาหลังจากโหลดหน้าต่างจากส่วนเกม

 /******** * GAME * ********/ ... window.onload = function() { ... addTreesRandomlyLoop(); }

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

ต้นไม้วางไข่แบบสุ่ม
ต้นไม้วางไข่แบบสุ่ม (ตัวอย่างขนาดใหญ่)

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

ในขั้นตอนต่อไป มาเพิ่มการทดสอบการชนกัน

ขั้นตอนที่ 3: การทดสอบการชน

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

ไปที่ index.html ลงไปที่ส่วน TREES เราจะเพิ่มข้อมูลเลนให้กับต้นไม้แต่ละต้น สำหรับแต่ละต้นไม้ เพิ่ม data-tree-position-index= ดังต่อไปนี้ เพิ่ม class="tree" เพื่อให้เราสามารถเลือกต้นไม้ทั้งหมดได้อย่างง่ายดาย:

 <a-entity data-tree-position-index="1" class="tree" ...> </a-entity> <a-entity data-tree-position-index="0" class="tree" ...> </a-entity> <a-entity data-tree-position-index="2" class="tree" ...> </a-entity>

ไปที่ asset/ergo.js และเรียกใช้ฟังก์ชัน setupCollisions ใหม่ในส่วน GAME นอกจากนี้ ให้กำหนดตัวแปรสากล isGameRunning ใหม่ที่ระบุว่าเกมที่มีอยู่กำลังทำงานอยู่แล้วหรือไม่

 /******** * GAME * ********/ var isGameRunning = false; setupControls(); setupCollision(); window.onload = function() { ...

กำหนดส่วนการ TREES COLLISIONS ก่อนส่วนเกม ในส่วนนี้ ให้กำหนดฟังก์ชัน setupCollisions

 /********* * TREES * *********/ ... /************** * COLLISIONS * **************/ const POSITION_Z_OUT_OF_SIGHT = 1; const POSITION_Z_LINE_START = 0.6; const POSITION_Z_LINE_END = 0.7; function setupCollision() { } /******** * GAME * ********/

เช่นเคย เราจะลงทะเบียนคอมโพเนนต์ AFRAME และใช้ตัวฟังเหตุการณ์ tick เพื่อเรียกใช้โค้ดในทุกขั้นตอน ในกรณีนี้ เราจะลงทะเบียนส่วนประกอบกับ player และทำการตรวจสอบกับต้นไม้ทั้งหมดใน Listener นั้น:

 function setupCollisions() { AFRAME.registerComponent('player', { tick: function() { document.querySelectorAll('.tree').forEach(function(tree) { } } } }

ใน for loop ให้เริ่มต้นด้วยการรับข้อมูลที่เกี่ยวข้องของ tree:

 document.querySelectorAll('.tree').forEach(function(tree) { position = tree.getAttribute('position'); tree_position_index = tree.getAttribute('data-tree-position-index'); tree_id = tree.getAttribute('id'); }

ถัดไป ยังอยู่ในลูป for ให้เอาต้นไม้ออกหากมองไม่เห็น ทันทีหลังจากดึงคุณสมบัติของต้นไม้ออก:

 document.querySelectorAll('.tree').forEach(function(tree) { ... if (position.z > POSITION_Z_OUT_OF_SIGHT) { removeTree(tree); } }

ต่อไป หากไม่มีเกมที่กำลังรันอยู่ อย่าตรวจสอบว่ามีการชนกันหรือไม่

 document.querySelectorAll('.tree').forEach(function(tree) { if (!isGameRunning) return; }

สุดท้าย (ยังอยู่ใน for loop) ให้ตรวจสอบว่าต้นไม้มีตำแหน่งเดียวกันกับผู้เล่นหรือไม่ ถ้าใช่ ให้เรียกใช้ฟังก์ชัน gameOver ที่ยังไม่ได้กำหนด:

 document.querySelectorAll('.tree').forEach(function(tree) { ... if (POSITION_Z_LINE_START < position.z && position.z < POSITION_Z_LINE_END && tree_position_index == player_position_index) { gameOver(); } }

ตรวจสอบว่าฟังก์ชัน setupCollisions ของคุณตรงกับรายการต่อไปนี้:

 function setupCollisions() { AFRAME.registerComponent('player', { tick: function() { document.querySelectorAll('.tree').forEach(function(tree) { position = tree.getAttribute('position'); tree_position_index = tree.getAttribute('data-tree-position-index'); tree_id = tree.getAttribute('id'); if (position.z > POSITION_Z_OUT_OF_SIGHT) { removeTree(tree); } if (!isGameRunning) return; if (POSITION_Z_LINE_START < position.z && position.z < POSITION_Z_LINE_END && tree_position_index == player_position_index) { gameOver(); } }) } }) }

นี่เป็นการสรุปการตั้งค่าการชนกัน ตอนนี้ เราจะเพิ่มสิ่งที่ดีสองสามอย่างเพื่อแยกลำดับ startGame และ gameOver ไปที่ส่วน GAME อัปเดตบล็อก window.onload ให้ตรงกับรายการต่อไปนี้ โดยแทนที่ addTreesRandomlyLoop ด้วยฟังก์ชัน startGame ที่ยังไม่ได้กำหนด

 window.onload = function() { setupTrees(); startGame(); }

ภายใต้การเรียกใช้ฟังก์ชันการตั้งค่า ให้สร้างฟังก์ชัน startGame ใหม่ ฟังก์ชันนี้จะเริ่มต้นตัวแปร isGameRunning ตามลำดับ และป้องกันการเรียกซ้ำซ้อน

 window.onload = function() { ... } function startGame() { if (isGameRunning) return; isGameRunning = true; addTreesRandomlyLoop(); }

สุดท้าย ให้นิยาม gameOver ซึ่งจะแจ้งเตือนว่า “Game Over!” ข้อความสำหรับตอนนี้

 function startGame() { ... } function gameOver() { isGameRunning = false; alert('Game Over!'); teardownTrees(); }

นี่เป็นการสรุปส่วนการทดสอบการชนกันของเกมวิ่งไม่รู้จบ

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

บทสรุป

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

ต่อไปนี้เป็นแหล่งข้อมูลเพิ่มเติมสำหรับการควบคุม VR และชุดหูฟังต่างๆ:

  • A-Frame สำหรับชุดหูฟัง VR
    แบบสำรวจเบราว์เซอร์และชุดหูฟังที่ A-Frame VR รองรับ
  • A-Frame สำหรับตัวควบคุม VR
    วิธีที่ A-Frame ไม่รองรับคอนโทรลเลอร์, คอนโทรลเลอร์ 3DoF และคอนโทรลเลอร์ 6DoF นอกเหนือจากทางเลือกอื่นสำหรับการโต้ตอบ

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