Fieldrunners
Fieldrunners เป็นเกมสไตล์ป้องกันฐานที่ได้รับรางวัล ซึ่งเปิดตัวครั้งแรกสำหรับ iPhone ในปี 2008 นับตั้งแต่นั้นมาก็มีการพอร์ตเกมนี้ไปยังแพลตฟอร์มอื่นๆ อีกมากมาย แพลตฟอร์มล่าสุดที่เปิดตัวคือเบราว์เซอร์ Chrome ในเดือนตุลาคม 2011 ความท้าทายอย่างหนึ่งในการพอร์ต Fieldrunners ไปยังแพลตฟอร์ม HTML5 คือวิธีเล่นเสียง
Fieldrunners ไม่ได้ใช้เอฟเฟกต์เสียงที่ซับซ้อน แต่ก็มีความคาดหวังบางอย่างเกี่ยวกับวิธีที่ผู้เล่นจะโต้ตอบกับเอฟเฟกต์เสียง เกมนี้มีเอฟเฟกต์เสียง 88 รายการ ซึ่งคาดว่าจะมีจำนวนมากที่เล่นพร้อมกัน เสียงเหล่านี้ส่วนใหญ่จะสั้นมากและต้องเล่นให้ตรงเวลามากที่สุดเพื่อไม่ให้เกิดความไม่สอดคล้องกับการนำเสนอแบบกราฟิก
พบปัญหาบางอย่าง
ขณะที่พอร์ต Fieldrunners ไปยัง HTML5 เราพบปัญหาเกี่ยวกับการเล่นเสียงด้วยแท็กเสียง และตัดสินใจตั้งแต่เนิ่นๆ ว่าจะมุ่งเน้นไปที่ Web Audio API แทน การใช้ WebAudio ช่วยเราแก้ปัญหาต่างๆ เช่น การให้เอฟเฟกต์ที่เกิดขึ้นพร้อมกันจำนวนมากที่ Fieldrunners ต้องการ อย่างไรก็ตาม ขณะที่พัฒนาระบบเสียงสำหรับ Fieldrunners HTML5 เราพบปัญหาเล็กน้อยที่นักพัฒนาซอฟต์แวร์รายอื่นๆ อาจต้องการทราบ
ลักษณะของ AudioBufferSourceNodes
AudioBufferSourceNode เป็นวิธีหลักในการเล่นเสียงด้วย WebAudio คุณควรทราบว่าโฆษณาคั่นระหว่างหน้าเป็นออบเจ็กต์ที่ใช้ได้ครั้งเดียว คุณสร้าง AudioBufferSourceNode, กำหนดบัฟเฟอร์, เชื่อมต่อกับกราฟ และเล่นด้วย noteOn หรือ noteGrainOn หลังจากนั้น คุณสามารถเรียกใช้ noteOff เพื่อหยุดการเล่น แต่จะเล่นแหล่งที่มาอีกครั้งโดยเรียกใช้ noteOn หรือ noteGrainOn ไม่ได้ คุณต้องสร้าง AudioBufferSourceNode อื่น คุณสามารถใช้ AudioBuffer ออบเจ็กต์พื้นฐานเดียวกันซ้ำได้ (อันที่จริง คุณสามารถมี AudioBufferSourceNode ที่ใช้งานอยู่หลายรายการซึ่งชี้ไปยัง AudioBuffer อินสแตนซ์เดียวกันได้ด้วย) คุณจะเห็นตัวอย่างการเล่นเกม Fieldrunners ใน Give Me a Beat
เนื้อหาที่ไม่มีการแคช
เมื่อเปิดตัว เซิร์ฟเวอร์ HTML5 ของ Fieldrunners แสดงคำขอจำนวนมากสำหรับไฟล์เพลง ผลลัพธ์นี้เกิดจาก Chrome 15 ที่ดำเนินการดาวน์โหลดไฟล์เป็นส่วนๆ แล้วไม่แคชไฟล์ ในตอนนั้น เราจึงตัดสินใจโหลดไฟล์เพลงเหมือนกับไฟล์เสียงอื่นๆ การดำเนินการดังกล่าวไม่เหมาะสม แต่เบราว์เซอร์อื่นๆ บางเวอร์ชันยังคงทำเช่นนี้อยู่
ปิดเสียงเมื่อไม่ได้โฟกัส
ก่อนหน้านี้ การตรวจจับเมื่อแท็บของเกมไม่ได้อยู่ในโฟกัสเป็นเรื่องยาก Fieldrunners เริ่มพอร์ตก่อน Chrome 13 ซึ่ง Page Visibility API เข้ามาแทนที่ความจำเป็นในการใช้โค้ดที่ซับซ้อนเพื่อตรวจจับการเบลอแท็บ ทุกเกมควรใช้ Visibility API เพื่อเขียนข้อมูลโค้ดขนาดเล็กเพื่อปิดเสียงหรือหยุดเสียงชั่วคราว หากไม่ได้หยุดทั้งเกมชั่วคราว เนื่องจาก Fieldrunners ใช้ requestAnimationFrame API การหยุดเกมชั่วคราวจึงได้รับการจัดการโดยนัย แต่ไม่ใช่การหยุดเสียงชั่วคราว
กำลังหยุดเสียงชั่วคราว
น่าแปลกที่ขณะรับความคิดเห็นเกี่ยวกับบทความนี้ เราได้รับแจ้งว่าเทคนิคที่เราใช้ในการหยุดเสียงชั่วคราวนั้นไม่เหมาะสม เราใช้ข้อบกพร่องในการติดตั้งใช้งาน Web Audio ในปัจจุบันเพื่อหยุดการเล่นเสียงชั่วคราว เนื่องจากปัญหานี้จะได้รับการแก้ไขในอนาคต คุณจึงไม่สามารถหยุดเสียงชั่วคราวได้โดยการยกเลิกการเชื่อมต่อโหนดหรือซับกราฟเพื่อหยุดการเล่นชั่วคราว
สถาปัตยกรรมโหนด Web Audio ที่เรียบง่าย
Fieldrunners มีโมเดลเสียงที่เรียบง่ายมาก โมเดลดังกล่าวรองรับชุดฟีเจอร์ต่อไปนี้
- ควบคุมระดับเสียงของเอฟเฟกต์เสียง
- ควบคุมระดับเสียงของแทร็กเพลงพื้นหลัง
- ปิดเสียงทั้งหมด
- ปิดเสียงที่เล่นเมื่อเกมหยุดชั่วคราว
- เปิดเสียงเหล่านั้นอีกครั้งเมื่อเกมกลับมาทำงานต่อ
- ปิดเสียงทั้งหมดเมื่อแท็บของเกมสูญเสียโฟกัส
- เริ่มเล่นอีกครั้งหลังจากเล่นเสียงตามต้องการ
เพื่อให้ได้ฟีเจอร์ข้างต้นด้วย Web Audio ระบบจึงใช้โหนดที่เป็นไปได้ 3 โหนด ได้แก่ DestinationNode, GainNode, AudioBufferSourceNode AudioBufferSourceNode จะเล่นเสียง GainNode จะเชื่อมต่อ AudioBufferSourceNode เข้าด้วยกัน DestinationNode ที่สร้างโดยบริบท Web Audio ซึ่งเรียกว่าปลายทางจะเล่นเสียงให้ผู้เล่น Web Audio มีโหนดอีกหลายประเภท แต่เราสามารถสร้างกราฟเสียงที่เรียบง่ายสำหรับเกมได้โดยใช้โหนดเหล่านี้เท่านั้น
กราฟโหนด Web Audio จะนำจากโหนดปลายสุดไปยังโหนดปลายทาง Fieldrunners ใช้โหนดอัตราขยายถาวร 6 โหนด แต่ 3 โหนดก็เพียงพอที่จะช่วยให้ควบคุมระดับเสียงได้ง่ายและเชื่อมต่อโหนดชั่วคราวจำนวนมากขึ้นซึ่งจะเล่นบัฟเฟอร์ อันดับแรก โหนดเกนหลักจะแนบโหนดย่อยทุกโหนดเข้ากับปลายทาง โหนดเกน 2 โหนดจะเชื่อมต่อกับโหนดเกนหลักโดยตรง โดยโหนดหนึ่งสำหรับช่องเพลง และอีกโหนดหนึ่งสำหรับลิงก์เอฟเฟกต์เสียงทั้งหมด
Fieldrunners มีโหนดเกนเพิ่มขึ้น 3 โหนดเนื่องจากการใช้ข้อบกพร่องเป็นฟีเจอร์อย่างไม่ถูกต้อง เราใช้โหนดเหล่านั้นเพื่อตัดกลุ่มเสียงที่เล่นออกจากกราฟ ซึ่งจะหยุดความคืบหน้าของเสียง เราดำเนินการนี้เพื่อหยุดเสียงชั่วคราว เนื่องจากไม่ถูกต้อง ตอนนี้เราจะใช้โหนดอัตราขยายทั้งหมดเพียง 3 โหนดตามที่อธิบายไว้ข้างต้น ตัวอย่างโค้ดต่อไปนี้จำนวนมากจะมีโหนดที่ไม่ถูกต้อง ซึ่งแสดงสิ่งที่เราทำและวิธีแก้ไขในระยะสั้น แต่ในระยะยาว คุณไม่ควรใช้โหนดของเราหลังจากโหนด coreEffectsGain
function AudioManager() {
// map for loaded sounds
this.sounds = {};
// create our permanent nodes
this.nodes = {
destination: this.audioContext.destination,
masterGain: this.audioContext.createGain(),
backgroundMusicGain: this.audioContext.createGain(),
coreEffectsGain: this.audioContext.createGain(),
effectsGain: this.audioContext.createGain(),
pausedEffectsGain: this.audioContext.createGain()
};
// and setup the graph
this.nodes.masterGain.connect( this.nodes.destination );
this.nodes.backgroundMusicGain.connect( this.nodes.masterGain );
this.nodes.coreEffectsGain.connect( this.nodes.masterGain );
this.nodes.effectsGain.connect( this.nodes.coreEffectsGain );
this.nodes.pausedEffectsGain.connect( this.nodes.coreEffectsGain );
}
เกมส่วนใหญ่จะอนุญาตให้ควบคุมเอฟเฟกต์เสียงและเพลงแยกกันได้ ซึ่งทำได้ง่ายๆ โดยใช้กราฟด้านบน โหนด Gain แต่ละโหนดมีแอตทริบิวต์ "Gain" ที่ตั้งค่าเป็นค่าทศนิยมใดก็ได้ระหว่าง 0 ถึง 1 ซึ่งใช้เพื่อควบคุมระดับเสียงได้ เนื่องจากเราต้องการควบคุมระดับเสียงของช่องเพลงและเอฟเฟกต์เสียงแยกกัน เราจึงมีโหนดเกนสำหรับแต่ละช่องที่เราควบคุมระดับเสียงได้
function setArbitraryVolume() {
var musicGainNode = this.nodes.backgroundMusicGain;
// set music volume to 50%
musicGainNode.gain.value = 0.5;
}
เราสามารถใช้ความสามารถเดียวกันนี้เพื่อควบคุมระดับเสียงของทุกอย่าง ทั้งเอฟเฟกต์เสียงและเพลง การตั้งค่าเกนของโหนดหลักจะส่งผลต่อเสียงทั้งหมดจากเกม หากตั้งค่าอัตราขยายเป็น 0 คุณจะปิดเสียงและเพลง AudioBufferSourceNode มีพารามิเตอร์อัตราขยายด้วยเช่นกัน คุณสามารถติดตามรายการเสียงที่เล่นอยู่ทั้งหมดและปรับค่าเกนของแต่ละเสียงเพื่อปรับระดับเสียงโดยรวมได้ หากคุณสร้างเอฟเฟกต์เสียงด้วยแท็กเสียง คุณจะต้องทำสิ่งนี้ แต่กราฟโหนดของ Web Audio ช่วยให้แก้ไขระดับเสียงของเสียงจำนวนมากได้ง่ายขึ้น การควบคุมระดับเสียงด้วยวิธีนี้ยังช่วยให้คุณมีพลังเสียงมากขึ้นโดยไม่มีความซับซ้อน เราสามารถแนบ AudioBufferSourceNode เข้ากับโหนดหลักโดยตรงเพื่อเล่นเพลงและควบคุมเกนของตัวเองได้ แต่คุณจะต้องตั้งค่านี้ทุกครั้งที่สร้าง AudioBufferSourceNode เพื่อวัตถุประสงค์ในการเล่นเพลง แต่คุณจะเปลี่ยนโหนดเดียวเมื่อผู้เล่นเปลี่ยนระดับเสียงเพลงและเมื่อเปิดเกมเท่านั้น ตอนนี้เรามีค่าเกนในแหล่งที่มาของบัฟเฟอร์เพื่อทำอย่างอื่นแล้ว สำหรับเพลง การใช้งานที่พบบ่อยอย่างหนึ่งคือการสร้างการเฟดไขว้จากแทร็กเสียงหนึ่งไปยังอีกแทร็กหนึ่งเมื่อแทร็กหนึ่งจบลงและอีกแทร็กหนึ่งเริ่มขึ้น Web Audio มีวิธีที่ยอดเยี่ยมในการดำเนินการนี้อย่างง่ายดาย
function arbitraryCrossfade( track1, track2 ) {
track1.gain.linearRampToValueAtTime( 0, 1 );
track2.gain.linearRampToValueAtTime( 1, 1 );
}
Fieldrunners ไม่ได้ใช้การครอสเฟดโดยเฉพาะ หากเราทราบถึงฟังก์ชันการตั้งค่าค่าของ WebAudio ในระหว่างการส่งผ่านระบบเสียงครั้งแรก เราก็คงจะใช้ฟังก์ชันนี้
กำลังหยุดเสียงชั่วคราว
เมื่อผู้เล่นหยุดเกมชั่วคราว เสียงบางอย่างอาจยังคงเล่นอยู่ เสียงเป็นส่วนสำคัญของความคิดเห็นสำหรับการกดองค์ประกอบอินเทอร์เฟซผู้ใช้ทั่วไปในเมนูเกม เนื่องจาก Fieldrunners มีอินเทอร์เฟซจำนวนมากให้ผู้ใช้โต้ตอบขณะที่เกมหยุดชั่วคราว เราจึงยังคงต้องการให้ผู้ใช้เล่นเกมต่อไป อย่างไรก็ตาม เราไม่ต้องการให้เสียงที่ยาวหรือเสียงวนซ้ำเล่นต่อไป การหยุดเสียงเหล่านั้นด้วย Web Audio เป็นเรื่องที่ค่อนข้างง่าย หรืออย่างน้อยเราก็คิดเช่นนั้น
AudioManager.prototype.pauseEffects = function() {
this.nodes.effectsGain.disconnect();
}
โหนดเอฟเฟกต์ที่หยุดชั่วคราวยังคงเชื่อมต่ออยู่ เสียงที่ได้รับอนุญาตให้ละเว้นสถานะหยุดชั่วคราวของเกมจะยังคงเล่นต่อไป เมื่อเกมกลับมาทำงานอีกครั้ง เราจะเชื่อมต่อโหนดเหล่านั้นอีกครั้งและเล่นเสียงทั้งหมดอีกครั้งได้ทันที
AudioManager.prototype.resumeEffects = function() {
this.nodes.effectsGain.connect( this.nodes.coreEffectsGain );
}
หลังจากเปิดตัว Fieldrunners เราพบว่าการยกเลิกการเชื่อมต่อโหนดหรือซับกราฟเพียงอย่างเดียวจะไม่หยุดการเล่น AudioBufferSourceNodes ชั่วคราว เราใช้ประโยชน์จากข้อบกพร่องใน WebAudio ซึ่งปัจจุบันหยุดการเล่นโหนดที่ไม่ได้เชื่อมต่อกับโหนดปลายทางในกราฟ ดังนั้น เพื่อให้แน่ใจว่าเราพร้อมสำหรับการแก้ไขในอนาคต เราจึงต้องมีโค้ดดังต่อไปนี้
AudioManager.prototype.pauseEffects = function() {
this.nodes.effectsGain.disconnect();
var now = Date.now();
for ( var name in this.sounds ) {
var sound = this.sounds[ name ];
if ( !sound.ignorePause && ( now - sound.source.noteOnAt < sound.buffer.duration * 1000 ) ) {
sound.pausedAt = now - sound.source.noteOnAt;
sound.source.noteOff();
}
}
}
AudioManager.prototype.resumeEffects = function() {
this.nodes.effectsGain.connect( this.nodes.coreEffectsGain );
var now = Date.now();
for ( var name in this.sounds ) {
if ( sound.pausedAt ) {
this.play( sound.name );
delete sound.pausedAt;
}
}
};
หากเราทราบเรื่องนี้เร็วกว่านี้ว่าเรากำลังใช้ประโยชน์จากข้อบกพร่อง โครงสร้างโค้ดเสียงของเราคงจะแตกต่างออกไปมาก ด้วยเหตุนี้ จึงส่งผลต่อหลายส่วนของบทความนี้ ซึ่งส่งผลโดยตรงที่นี่ รวมถึงในข้อมูลโค้ดใน Losing Focus และ Give Me a Beat ด้วย การทำความเข้าใจวิธีการทำงานของฟีเจอร์นี้จำเป็นต้องมีการเปลี่ยนแปลงทั้งในกราฟโหนด Fieldrunners (เนื่องจากเราสร้างโหนดสำหรับการลัดวงจรการเล่น) และโค้ดเพิ่มเติมที่จะบันทึกและระบุสถานะที่หยุดชั่วคราวซึ่ง Web Audio ไม่ได้ทำเอง
สูญเสียโฟกัส
โหนดหลักของเราจะเข้ามามีบทบาทในฟีเจอร์นี้ เมื่อผู้ใช้เบราว์เซอร์เปลี่ยนไปใช้แท็บอื่น ผู้ใช้จะไม่เห็นเกมอีกต่อไป เมื่อไม่เห็นก็ไม่กวนใจ และเสียงก็ควรจะหายไปด้วย มีเคล็ดลับที่สามารถใช้เพื่อระบุสถานะระดับการเข้าถึงที่เฉพาะเจาะจงสำหรับหน้าของเกม แต่ Visibility API จะช่วยให้ทำได้ง่ายขึ้นมาก
Fieldrunners จะเล่นเป็นแท็บที่ใช้งานอยู่เท่านั้นเนื่องจากใช้ requestAnimationFrame เพื่อเรียกใช้ลูปการอัปเดต แต่บริบท Web Audio จะยังคงเล่นเอฟเฟกต์แบบวนซ้ำและแทร็กพื้นหลังต่อไปขณะที่ผู้ใช้อยู่ในแท็บอื่น แต่เราสามารถหยุดการทำงานดังกล่าวได้ด้วยข้อมูลโค้ดที่รับรู้ Visibility API ขนาดเล็กมาก
function AudioManager() {
// map and node setup
// ...
// disable all sound when on other tabs
var self = this;
window.addEventListener( 'webkitvisibilitychange', function( e ) {
if ( document.webkitHidden ) {
self.nodes.masterGain.disconnect();
// As noted in Pausing Sounds disconnecting isn't enough.
// For Fieldrunners calling our new pauseEffects method would be
// enough to accomplish that, though we may still need some logic
// to not resume if already paused.
self.pauseEffects();
} else {
self.nodes.masterGain.connect( this.nodes.destination );
self.resumeEffects();
}
});
}
ก่อนที่จะเขียนบทความนี้ เราคิดว่าการยกเลิกการเชื่อมต่ออุปกรณ์หลักน่าจะเพียงพอที่จะหยุดเสียงทั้งหมดชั่วคราวแทนการปิดเสียง การยกเลิกการเชื่อมต่อโหนดในขณะนั้นทำให้เราหยุดโหนดและโหนดย่อยไม่ให้ประมวลผลและเล่นได้ เมื่อเชื่อมต่ออีกครั้ง เสียงและเพลงทั้งหมดจะเริ่มเล่นจากจุดที่ค้างไว้ เช่นเดียวกับเกมเพลย์ที่จะเล่นต่อจากจุดที่ค้างไว้ แต่ลักษณะการทำงานนี้เป็นสิ่งที่ไม่คาดคิด การยกเลิกการเชื่อมต่อเพียงอย่างเดียวไม่เพียงพอที่จะหยุดการเล่นชั่วคราว
Page Visibility API ช่วยให้ทราบได้ง่ายๆ ว่าแท็บไม่ได้อยู่ในโฟกัสอีกต่อไป หากคุณมีโค้ดที่มีประสิทธิภาพในการหยุดเสียงชั่วคราวอยู่แล้ว คุณจะใช้โค้ดเพียงไม่กี่บรรทัดในการเขียนโค้ดหยุดเสียงชั่วคราวเมื่อแท็บเกมถูกซ่อน
ขอเสียงบีทบ็อกซ์หน่อย
ตอนนี้เราได้ตั้งค่าบางอย่างแล้ว เรามีกราฟของโหนด เราสามารถหยุดเสียงชั่วคราวเมื่อผู้เล่นหยุดเกมชั่วคราว และเล่นเสียงใหม่สำหรับองค์ประกอบต่างๆ เช่น เมนูเกม เราสามารถหยุดเสียงและเพลงทั้งหมดชั่วคราวเมื่อผู้ใช้เปลี่ยนไปใช้แท็บใหม่ ตอนนี้เราต้องเล่นเสียงจริงๆ
Fieldrunners จะเล่นเสียงเพียงครั้งเดียวตลอดระยะเวลาของเสียง แทนที่จะเล่นเสียงซ้ำหลายครั้งสำหรับเอนทิตีเกมหลายรายการ เช่น ตัวละครตาย หากต้องการเสียงหลังจากที่เล่นเสร็จแล้ว ก็สามารถเริ่มใหม่ได้ แต่จะเริ่มใหม่ขณะที่กำลังเล่นอยู่ไม่ได้ นี่เป็นการตัดสินใจสำหรับการออกแบบเสียงของ Fieldrunners เนื่องจากมีเสียงที่ขอให้เล่นอย่างรวดเร็ว ซึ่งอาจทำให้เสียงกระตุกหากอนุญาตให้รีสตาร์ท หรือสร้างเสียงที่น่ารำคาญหากอนุญาตให้เล่นหลายอินสแตนซ์ คาดว่าจะมีการใช้ AudioBufferSourceNodes เป็นแบบครั้งเดียว สร้างโหนด แนบบัฟเฟอร์ ตั้งค่าบูลีนของลูปหากจำเป็น เชื่อมต่อกับโหนดในกราฟที่จะนำไปสู่ปลายทาง เรียก noteOn หรือ noteGrainOn และเรียก noteOff (ไม่บังคับ)
สำหรับ Fieldrunners จะมีลักษณะดังนี้
AudioManager.prototype.play = function( options ) {
var now = Date.now(),
// pull from a map of loaded audio buffers
sound = this.sounds[ options.name ],
channel,
source,
resumeSource;
if ( !sound ) {
return;
}
if ( sound.source ) {
var source = sound.source;
if ( !options.loop && now - source.noteOnAt > sound.buffer.duration * 1000 ) {
// discard the previous source node
source.stop( 0 );
source.disconnect();
} else {
return;
}
}
source = this.audioContext.createBufferSource();
sound.source = source;
// track when the source is started to know if it should still be playing
source.noteOnAt = now;
// help with pausing
sound.ignorePause = !!options.ignorePause;
if ( options.ignorePause ) {
channel = this.nodes.pausedEffectsGain;
} else {
channel = this.nodes.effectsGain;
}
source.buffer = sound.buffer;
source.connect( channel );
source.loop = options.loop || false;
// Fieldrunners' current code doesn't consider sound.pausedAt.
// This is an added section to assist the new pausing code.
if ( sound.pausedAt ) {
source.start( ( sound.buffer.duration * 1000 - sound.pausedAt ) / 1000 );
source.noteOnAt = now + sound.buffer.duration * 1000 - sound.pausedAt;
// if you needed to precisely stop sounds, you'd want to store this
resumeSource = this.audioContext.createBufferSource();
resumeSource.buffer = sound.buffer;
resumeSource.connect( channel );
resumeSource.start(
0,
sound.pausedAt,
sound.buffer.duration - sound.pausedAt / 1000
);
} else {
// start play immediately with a value of 0 or less
source.start( 0 );
}
}
สตรีมมิงมากเกินไป
Fieldrunners เปิดตัวครั้งแรกโดยมีเพลงพื้นหลังที่เล่นด้วยแท็กเสียง เมื่อเปิดตัว เราพบว่ามีการขอไฟล์เพลงเป็นจำนวนมากเกินสัดส่วนเมื่อเทียบกับเนื้อหาเกมส่วนที่เหลือ หลังจากทำการค้นคว้าข้อมูล เราพบว่าในขณะนั้นเบราว์เซอร์ Chrome ไม่ได้แคชส่วนที่สตรีมของไฟล์เพลง ซึ่งส่งผลให้เบราว์เซอร์ขอแทร็กที่เล่นทุกๆ 2-3 นาทีเมื่อเล่นเสร็จ ในการทดสอบล่าสุด Chrome ได้แคชแทร็กที่สตรีมไว้ แต่เบราว์เซอร์อื่นๆ อาจยังไม่ได้ดำเนินการนี้ การสตรีมไฟล์เสียงขนาดใหญ่ด้วยแท็กเสียงสำหรับฟังก์ชันการทำงาน เช่น การเล่นเพลงนั้นเหมาะสมที่สุด แต่สำหรับเบราว์เซอร์บางเวอร์ชัน คุณอาจต้องการโหลดเพลงในลักษณะเดียวกับที่โหลดเอฟเฟกต์เสียง
เนื่องจากเอฟเฟกต์เสียงทั้งหมดเล่นผ่าน Web Audio เราจึงย้ายการเล่นเพลงพื้นหลังไปยัง Web Audio ด้วย ซึ่งหมายความว่าเราจะโหลดแทร็กในลักษณะเดียวกับที่เราโหลดเอฟเฟกต์ทั้งหมดด้วย XMLHttpRequest และประเภทการตอบกลับ arraybuffer
AudioManager.prototype.load = function( options ) {
var xhr,
// pull from a map of name, object pairs
sound = this.sounds[ options.name ];
if ( sound ) {
// this is a great spot to add success methods to a list or use promises
// for handling the load event or call success if already loaded
if ( sound.buffer && options.success ) {
options.success( options.name );
} else if ( options.success ) {
sound.success.push( options.success );
}
// one buffer is enough so shortcut here
return;
}
sound = {
name: options.name,
buffer: null,
source: null,
success: ( options.success ? [ options.success ] : [] )
};
this.sounds[ options.name ] = sound;
xhr = new XMLHttpRequest();
xhr.open( 'GET', options.path, true );
xhr.responseType = 'arraybuffer';
xhr.onload = function( e ) {
sound.buffer = self._context.createBuffer( xhr.response, false );
// call all waiting handlers
sound.success.forEach( function( success ) {
success( sound.name );
});
delete sound.success;
};
xhr.onerror = function( e ) {
// failures are uncommon but you want to do deal with them
};
xhr.send();
}
สรุป
Fieldrunners เป็นเกมที่สนุกมากเมื่อนำมาเล่นใน Chrome และ HTML5 นอกเหนือจากงานจำนวนมหาศาลในการนำโค้ด C++ หลายพันบรรทัดมาใช้ใน JavaScript แล้ว ยังมีปัญหาที่น่าสนใจและต้องตัดสินใจเกี่ยวกับ HTML5 โดยเฉพาะด้วย หากต้องการย้ำอีกครั้ง AudioBufferSourceNode เป็นออบเจ็กต์ที่ใช้ได้ครั้งเดียว สร้างบัฟเฟอร์เสียง แนบเข้ากับกราฟ Web Audio และเล่นด้วย noteOn หรือ noteGrainOn หากต้องการเล่นเสียงนั้นอีกครั้ง จากนั้นสร้าง AudioBufferSourceNode อีกรายการ