เกริ่นนำ
HTML5 มี API ที่มีประโยชน์จำนวนมากสำหรับการสร้างเว็บแอปพลิเคชันที่ทันสมัย ตอบสนองได้ และมีประสิทธิภาพในเบราว์เซอร์ ยอดเยี่ยมไปเลย แต่คุณก็อยากสร้างและเล่นเกมจริงๆ แต่โชคดีที่ HTML5 ได้ถือกำเนิดขึ้นในยุคใหม่ของการพัฒนาเกมที่ใช้ API อย่าง Canvas และเครื่องมือ JavaScript อันทรงพลังในการส่งมอบเกมไปยังเบราว์เซอร์ของคุณโดยตรงโดยไม่ต้องใช้ปลั๊กอิน
บทความนี้จะอธิบายเกี่ยวกับการสร้างคอมโพเนนต์การจัดการเนื้อหาอย่างง่ายสำหรับเกม HTML5 หากไม่มีเครื่องมือจัดการเนื้อหา เกมของคุณจะพบปัญหาในการชดเชยเวลาดาวน์โหลดที่ไม่ทราบและการโหลดรูปภาพแบบไม่พร้อมกัน ทำตามขึ้นมาเพื่อดูตัวอย่างเครื่องมือจัดการเนื้อหาแบบง่ายสำหรับเกม HTML5
ตัวปัญหา
เกม HTML5 ไม่สามารถคาดเดาได้ว่าเนื้อหา เช่น รูปภาพหรือเสียงจะปรากฏในเครื่องของผู้เล่น เนื่องจากเกม HTML5 บอกเป็นนัยว่าการเล่นในเว็บเบราว์เซอร์มีเนื้อหาที่ดาวน์โหลดผ่าน HTTP เนื่องจากมีเครือข่ายเข้ามาเกี่ยวข้อง เบราว์เซอร์จึงไม่แน่ใจว่าเนื้อหาสำหรับเกมจะดาวน์โหลดและใช้ได้เมื่อใด
วิธีพื้นฐานในการโหลดรูปภาพในเว็บเบราว์เซอร์โดยใช้โปรแกรมคือโค้ดต่อไปนี้
var image = new Image();
image.addEventListener("success", function(e) {
// do stuff with the image
});
image.src = "/some/image.png";
ทีนี้ลองนึกภาพว่ามีภาพเป็นร้อยที่ต้องโหลดและแสดงเมื่อเกมเริ่มต้นขึ้น คุณจะรู้ได้อย่างไรว่ารูปภาพทั้ง 100 รูปพร้อมแล้ว ทุกคนโหลดสําเร็จใช่ไหม เกมนี้ควรเริ่มตอนไหน
โซลูชัน
ให้ผู้จัดการเนื้อหาจัดการคิวของเนื้อหาและรายงานกลับไปยังเกมเมื่อทุกอย่างพร้อมแล้ว ผู้จัดการเนื้อหาจะสรุปตรรกะสำหรับการโหลดเนื้อหาผ่านเครือข่าย และให้วิธีการง่ายในการตรวจสอบสถานะ
ผู้จัดการเนื้อหาแบบง่ายของเรามีข้อกำหนดต่อไปนี้
- จัดคิวการดาวน์โหลด
- เริ่มดาวน์โหลด
- ติดตามความสำเร็จและความล้มเหลว
- เป็นสัญญาณเมื่อทุกอย่างเสร็จเรียบร้อยแล้ว
- ดึงข้อมูลเนื้อหาได้ง่าย
การจัดคิว
ข้อกำหนดแรกคือการจัดลำดับการดาวน์โหลด การออกแบบนี้ช่วยให้คุณประกาศเนื้อหาที่ต้องการได้โดยไม่ต้องดาวน์โหลดจริง วิธีนี้มีประโยชน์ในกรณีที่ต้องการประกาศเนื้อหาทั้งหมดสําหรับระดับเกมในไฟล์การกำหนดค่า
โค้ดสำหรับตัวสร้างและการจัดคิวจะมีลักษณะดังนี้
function AssetManager() {
this.downloadQueue = [];
}
AssetManager.prototype.queueDownload = function(path) {
this.downloadQueue.push(path);
}
เริ่มดาวน์โหลด
หลังจากจัดคิวเนื้อหาทั้งหมดที่จะดาวน์โหลดแล้ว คุณขอให้ผู้จัดการเนื้อหาเริ่มดาวน์โหลดทุกอย่างได้
โชคดีที่เว็บเบราว์เซอร์สามารถโหลดการดาวน์โหลดพร้อมกันได้ ซึ่งโดยทั่วไปจะมีการเชื่อมต่อได้สูงสุด 4 รายการต่อโฮสต์ วิธีหนึ่งในการเพิ่มความเร็วในการดาวน์โหลดเนื้อหาคือการใช้ชื่อโดเมนที่หลากหลายสำหรับการโฮสต์เนื้อหา ตัวอย่างเช่น แทนที่จะแสดงทุกอย่างจาก assets.example.com ให้ลองใช้ assets1.example.com, assets2.example.com, assets3.example.com และอื่นๆ แม้ว่าชื่อโดเมนแต่ละชื่อจะเป็น CNAME ไปยังเว็บเซิร์ฟเวอร์เดียวกัน แต่เว็บเบราว์เซอร์จะมองว่าเป็นเซิร์ฟเวอร์ที่แยกกันและเพิ่มจำนวนการเชื่อมต่อซึ่งใช้สำหรับการดาวน์โหลดเนื้อหา ดูข้อมูลเพิ่มเติมเกี่ยวกับเทคนิคนี้ได้จากแยกคอมโพเนนต์ข้ามโดเมนในแนวทางปฏิบัติแนะนำสำหรับการเพิ่มความเร็วเว็บไซต์
วิธีเริ่มต้นการดาวน์โหลดของเราเรียกว่า downloadAll()
เราจะพัฒนาอย่างต่อเนื่อง สำหรับตอนนี้ นี่คือเหตุผลแรกในการเริ่มต้นการดาวน์โหลด
AssetManager.prototype.downloadAll = function() {
for (var i = 0; i < this.downloadQueue.length; i++) {
var path = this.downloadQueue[i];
var img = new Image();
var that = this;
img.addEventListener("load", function() {
// coming soon
}, false);
img.src = path;
}
}
ดังที่เห็นในโค้ดด้านบน downloadAll()
เพียงทำซ้ำตาม DownloadQueue และสร้างออบเจ็กต์รูปภาพใหม่ มีการเพิ่ม Listener เหตุการณ์สำหรับเหตุการณ์การโหลดและตั้งค่า src ของรูปภาพ ซึ่งทริกเกอร์การดาวน์โหลดจริง
เมื่อใช้วิธีนี้คุณสามารถเริ่มการดาวน์โหลดได้
การติดตามความสำเร็จและความล้มเหลว
ข้อกำหนดอีกอย่างคือการติดตามทั้งความสำเร็จและความล้มเหลว เพราะว่าทุกอย่างอาจทำงานได้ไม่สมบูรณ์แบบเสมอไป ตอนนี้โค้ดจะติดตามเฉพาะเนื้อหาที่ดาวน์โหลดสำเร็จเท่านั้น การเพิ่ม Listener เหตุการณ์สำหรับเหตุการณ์ข้อผิดพลาดจะทำให้คุณบันทึกได้ทั้งสถานการณ์สำเร็จและล้มเหลว
AssetManager.prototype.downloadAll = function(downloadCallback) {
for (var i = 0; i < this.downloadQueue.length; i++) {
var path = this.downloadQueue[i];
var img = new Image();
var that = this;
img.addEventListener("load", function() {
// coming soon
}, false);
img.addEventListener("error", function() {
// coming soon
}, false);
img.src = path;
}
}
ผู้จัดการเนื้อหาของเราต้องทราบว่าเราพบความสำเร็จและความล้มเหลวมากน้อยเพียงใด มิเช่นนั้นก็จะไม่มีทางทราบว่าเกมจะเริ่มได้เมื่อใด
ก่อนอื่น เราจะเพิ่มตัวนับให้กับออบเจ็กต์ในตัวสร้าง ซึ่งตอนนี้จะมีลักษณะดังนี้
function AssetManager() {
<span class="highlight"> this.successCount = 0;
this.errorCount = 0;</span>
this.downloadQueue = [];
}
จากนั้น เพิ่มตัวนับใน Listener เหตุการณ์ ซึ่งตอนนี้จะมีลักษณะดังนี้
img.addEventListener("load", function() {
<span class="highlight">that.successCount += 1;</span>
}, false);
img.addEventListener("error", function() {
<span class="highlight">that.errorCount += 1;</span>
}, false);
ตอนนี้ผู้จัดการเนื้อหากำลังติดตามทั้งเนื้อหาที่โหลดสำเร็จและล้มเหลว
การส่งสัญญาณเมื่อดำเนินการเสร็จสิ้น
หลังจากเกมจัดคิวเนื้อหาสำหรับดาวน์โหลดแล้ว และขอให้ผู้จัดการเนื้อหาดาวน์โหลดเนื้อหาทั้งหมด จะต้องแจ้งเกมเมื่อมีการดาวน์โหลดชิ้นงานทั้งหมดแล้ว แทนที่จะที่เกมจะถามซ้ำไปซ้ำมาว่าดาวน์โหลดเนื้อหาเสร็จแล้วหรือไม่ ผู้จัดการเนื้อหาสามารถส่งสัญญาณกลับไปยังเกมได้
ผู้จัดการเนื้อหาต้องรู้ก่อนว่าชิ้นงานทุกรายการเสร็จเมื่อใด เราจะเพิ่มเมธอด isDone ในตอนนี้
AssetManager.prototype.isDone = function() {
return (this.downloadQueue.length == this.successCount + this.errorCount);
}
การเปรียบเทียบความสำเร็จจากจำนวนความสำเร็จและข้อผิดพลาดกับขนาดของคิวดาวน์โหลดจะทำให้เครื่องมือจัดการเนื้อหารู้ว่าเนื้อหาทุกรายการเสร็จสมบูรณ์หรือมีข้อผิดพลาดบางอย่างหรือไม่
แน่นอนว่าการทราบว่าการกระทำเสร็จสิ้นเป็นเพียงแค่ครึ่งทางเท่านั้น ผู้จัดการเนื้อหาก็ต้องตรวจสอบวิธีการนี้ด้วยเช่นกัน เราจะเพิ่มการตรวจสอบนี้ภายในตัวแฮนเดิลเหตุการณ์ทั้ง 2 ตัวตามที่เห็นในโค้ดด้านล่าง
img.addEventListener("load", function() {
console.log(this.src + ' is loaded');
that.successCount += 1;
if (that.isDone()) {
// ???
}
}, false);
img.addEventListener("error", function() {
that.errorCount += 1;
if (that.isDone()) {
// ???
}
}, false);
หลังจากเพิ่มตัวนับแล้ว เราจะดูว่านั่นเป็นเนื้อหาสุดท้ายในคิวหรือไม่ หากผู้จัดการเนื้อหาดาวน์โหลดเสร็จแล้ว เราควรทำอย่างไร
แน่นอนว่าหากผู้จัดการเนื้อหาดาวน์โหลดเนื้อหาทั้งหมดเสร็จแล้ว เราจะเรียกใช้วิธีเรียกกลับ มาเปลี่ยน downloadAll()
และเพิ่มพารามิเตอร์สำหรับโค้ดเรียกกลับกัน
AssetManager.prototype.downloadAll = function(downloadCallback) {
...
เราจะเรียกเมธอด DownloadCallback ภายใน Listener เหตุการณ์
img.addEventListener("load", function() {
that.successCount += 1;
if (that.isDone()) {
downloadCallback();
}
}, false);
img.addEventListener("error", function() {
that.errorCount += 1;
if (that.isDone()) {
downloadCallback();
}
}, false);
ผู้จัดการชิ้นงานพร้อมสำหรับข้อกำหนดล่าสุดแล้ว
ดึงข้อมูลเนื้อหาได้ง่ายๆ
เมื่อมีการส่งสัญญาณว่าเกมเริ่มได้ เกมจะเริ่มแสดงรูปภาพ ผู้จัดการเนื้อหาไม่เพียงแต่มีหน้าที่ดาวน์โหลดและติดตามเนื้อหาเท่านั้น แต่ยังมีหน้าที่ส่งเนื้อหาเหล่านี้ไปยังเกมด้วย
ข้อกำหนดสุดท้ายของเรากล่าวถึงเมธอด getAsset บางประเภท ดังนั้นเราจะเพิ่มเนื้อหาดังกล่าวลงในตอนนี้
AssetManager.prototype.getAsset = function(path) {
return this.cache[path];
}
ออบเจ็กต์แคชนี้ได้เริ่มต้นในตัวสร้างแล้ว ซึ่งตอนนี้จะมีลักษณะดังนี้
function AssetManager() {
this.successCount = 0;
this.errorCount = 0;
this.cache = {};
this.downloadQueue = [];
}
แคชจะขึ้นตอนจบ downloadAll()
ตามที่แสดงด้านล่าง:
AssetManager.prototype.downloadAll = function(downloadCallback) {
...
img.addEventListener("error", function() {
that.errorCount += 1;
if (that.isDone()) {
downloadCallback();
}
}, false);
img.src = path;
<span class="highlight">this.cache[path] = img;</span>
}
}
โบนัส: แก้ไขข้อบกพร่อง
คุณพบข้อบกพร่องไหม ตามที่ได้เขียนไว้ข้างต้น ระบบจะเรียกใช้เมธอด isDone เมื่อมีการทริกเกอร์เหตุการณ์การโหลดหรือข้อผิดพลาดเท่านั้น แต่จะเกิดอะไรขึ้นหากผู้จัดการเนื้อหาไม่มีเนื้อหาใดๆ ที่รอดาวน์โหลดอยู่ ระบบจะไม่ทริกเกอร์เมธอด isDone และเกมไม่เริ่มเล่น
คุณสามารถดูแลสถานการณ์นี้ได้ด้วยการเพิ่มโค้ดต่อไปนี้ลงใน downloadAll()
:
AssetManager.prototype.downloadAll = function(downloadCallback) {
if (this.downloadQueue.length === 0) {
downloadCallback();
}
...
หากไม่มีเนื้อหาอยู่ในคิว ระบบจะเรียกใช้โค้ดเรียกกลับทันที แก้ไขข้อบกพร่องแล้ว!
ตัวอย่างการใช้งาน
การใช้ตัวจัดการเนื้อหานี้ในเกม HTML5 นั้นค่อนข้างตรงไปตรงมา ต่อไปนี้คือวิธีพื้นฐานที่สุดในการใช้ไลบรารี
var ASSET_MANAGER = new AssetManager();
ASSET_MANAGER.queueDownload('img/earth.png');
ASSET_MANAGER.downloadAll(function() {
var sprite = ASSET_MANAGER.getAsset('img/earth.png');
ctx.drawImage(sprite, x - sprite.width/2, y - sprite.height/2);
});
โค้ดด้านบนแสดงข้อมูลต่อไปนี้
- สร้างตัวจัดการชิ้นงานใหม่
- จัดคิวเนื้อหาที่จะดาวน์โหลด
- เริ่มดาวน์โหลดด้วย
downloadAll()
- ส่งสัญญาณเมื่อเนื้อหาพร้อมแล้วโดยเรียกใช้ฟังก์ชันเรียกกลับ
- ดึงข้อมูลชิ้นงานด้วย
getAsset()
สิ่งที่ยังพัฒนาได้อีก
เครื่องมือจัดการชิ้นงานที่เรียบง่ายนี้พัฒนาขึ้นมาได้อย่างแน่นอน ไม่ว่าคุณจะสร้างเกมขึ้นมา แต่ฉันหวังว่านี่จะเป็นการเริ่มเกมพื้นฐาน ฟีเจอร์ในอนาคตอาจประกอบด้วย
- ส่งสัญญาณว่าชิ้นงานใดมีข้อผิดพลาด
- โค้ดเรียกกลับเพื่อแสดงความคืบหน้า
- ดึงข้อมูลเนื้อหาจาก File System API
โปรดโพสต์การปรับปรุง ส้อม และลิงก์ไปยังโค้ดในความคิดเห็นด้านล่าง
แหล่งที่มาแบบเต็ม
แหล่งที่มาของผู้จัดการเนื้อหานี้และเกมที่เรียกที่มานั้นเป็นโอเพนซอร์สภายใต้การอนุญาตให้ใช้สิทธิ Apache และดูได้ในบัญชี Bad Aliens GitHub เกม Bad Aliens เล่นได้ในเบราว์เซอร์ที่เข้ากันได้กับ HTML5 เกมนี้เป็นหัวข้อการพูดคุยของ Google IO ที่ชื่อ Super Browsing 2 Turbo HD Remix: ข้อมูลเบื้องต้นเกี่ยวกับการพัฒนาเกม HTML5 (สไลด์ วิดีโอ)
สรุป
เกมส่วนใหญ่จะมีเครื่องมือจัดการเนื้อหาบางประเภท แต่เกม HTML5 ต้องใช้ตัวจัดการเนื้อหาที่โหลดเนื้อหาผ่านเครือข่ายและจัดการกับความล้มเหลว บทความนี้กล่าวถึงเครื่องมือจัดการเนื้อหาแบบง่ายที่ควรใช้งานและปรับให้เข้ากับเกม HTML5 ถัดไปได้ง่ายๆ ขอให้สนุก และโปรดบอกให้เราทราบว่าคุณคิดอย่างไรในความคิดเห็นด้านล่าง ขอขอบคุณ