การพัฒนาเสียงในเกมด้วย Web Audio API

เกริ่นนำ

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

เกมก็เช่นกัน ความทรงจำในวิดีโอเกมที่ฉันชื่นชอบที่สุดคือเพลง และเอฟเฟกต์เสียง ตอนนี้แม้จะเล่นเกมโปรดของฉันไปแล้วเกือบ 2 ทศวรรษ ฉันก็ยังคงฟังการเรียบเรียงเพลง Zelda และซาวด์แทร็ก Diablo ของ Matt Uelmen ไม่ได้ โดยมีความน่าสนใจในลักษณะเดียวกันสำหรับเอฟเฟกต์เสียงด้วย เช่น เสียงตอบรับคลิกจากเกมที่ทุกคนเห็นได้ทันทีจาก Warcraft และตัวอย่างจากเกมคลาสสิกของ Nintendo

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

เสียงเกมบนเว็บ

สำหรับเกมง่ายๆ การใช้แท็ก <audio> อาจเพียงพอแล้ว อย่างไรก็ตาม เบราว์เซอร์จำนวนมากมีการใช้งานที่ไม่ดี ซึ่งส่งผลให้เสียงบกพร่องและเวลาในการตอบสนองสูง นี่หวังว่าจะเป็นปัญหาชั่วคราว เนื่องจากผู้ให้บริการกำลังทำงานอย่างหนักเพื่อปรับปรุงการใช้งานที่เกี่ยวข้อง หากต้องการดูสถานะของแท็ก <audio> เรามีชุดทดสอบดีๆ ที่ areweplayingyet.org

อย่างไรก็ตาม เมื่อดูลึกลงไปในข้อกำหนดของแท็ก <audio> ก็เห็นได้ชัดว่ามีหลายสิ่งที่ทำไม่ได้ ซึ่งไม่น่าแปลกใจเนื่องจากออกแบบมาสำหรับการเล่นสื่อ ข้อจำกัดบางประการมีดังนี้

  • ใช้ตัวกรองกับสัญญาณเสียงไม่ได้
  • ไม่มีวิธีเข้าถึงข้อมูลดิบ PCM
  • ไม่มีแนวคิดเกี่ยวกับตำแหน่งและทิศทางของแหล่งที่มาและผู้ฟัง
  • ไม่มีช่วงเวลาแบบละเอียด

ในส่วนที่เหลือของบทความ ฉันจะอธิบายหัวข้อเหล่านี้ในบริบทของเสียงในเกมที่เขียนด้วย Web Audio API สำหรับข้อมูลเบื้องต้นสั้นๆ เกี่ยวกับ API นี้ โปรดดูที่บทแนะนำการเริ่มต้นใช้งาน

เพลงประกอบ

เกมมักมีเพลงที่เล่นวนซ้ำ

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

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

สายรัดข้อมือ

จากนั้นใช้ Web Audio API เพื่อนำเข้าตัวอย่างเหล่านี้ทั้งหมดโดยใช้คลาส เช่น คลาส BufferLoader ผ่าน XHR (อธิบายอย่างละเอียดในบทความ Web Audio API เบื้องต้น เสียงเมื่อโหลดต้องใช้เวลา จึงควรโหลดเนื้อหาที่ใช้ในเกมเมื่อโหลดหน้าเว็บ เริ่มด่าน หรืออาจเพิ่มขึ้นเรื่อยๆ ขณะที่ผู้เล่นกำลังเล่น

ถัดไป คุณสร้างแหล่งที่มาสำหรับแต่ละโหนด สร้างโหนดเกนสำหรับแหล่งที่มาแต่ละรายการ และเชื่อมต่อกราฟ

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

// Assume gains is an array of AudioGainNode, normVal is the intensity
// between 0 and 1.
var value = normVal - (gains.length - 1);
// First reset gains on all nodes.
for (var i = 0; i < gains.length; i++) {
    gains[i].gain.value = 0;
}
// Decide which two nodes we are currently between, and do an equal
// power crossfade between them.
var leftNode = Math.floor(value);
// Normalize the value between 0 and 1.
var x = value - leftNode;
var gain1 = Math.cos(x - 0.5*Math.PI);
var gain2 = Math.cos((1.0 - x) - 0.5*Math.PI);
// Set the two gains accordingly.
gains[leftNode].gain.value = gain1;
// Check to make sure that there's a right node.
if (leftNode < gains.length - 1) {
    // If there is, adjust its gain.
    gains[leftNode + 1].gain.value = gain2;
}

ในวิธีการข้างต้น แหล่งที่มา 2 รายการจะเล่นพร้อมกัน และเราจะค่อยๆ เฟดระหว่างแหล่งที่มาเหล่านั้นโดยใช้เส้นโค้งกำลังเท่ากัน (ตามที่อธิบายไว้ในบทนำ)

ปัจจุบันนักพัฒนาเกมหลายรายใช้แท็ก <audio> สำหรับเพลงพื้นหลังของตน เนื่องจากแท็กดังกล่าวเหมาะกับการสตรีมเนื้อหาอย่างมาก ตอนนี้คุณนำเนื้อหาจากแท็ก <audio> ไปไว้ในบริบทของ Web Audio ได้แล้ว

เทคนิคนี้อาจเป็นประโยชน์เนื่องจากแท็ก <audio> ใช้ได้กับเนื้อหาสตรีมมิง ซึ่งจะช่วยให้คุณเล่นเพลงพื้นหลังได้ทันทีโดยไม่ต้องรอให้ดาวน์โหลดทั้งหมด การนำสตรีมไปไว้ใน Web Audio API จะทำให้คุณจัดการหรือวิเคราะห์สตรีมได้ ตัวอย่างต่อไปนี้ใช้ตัวกรองระดับต่ำกับเพลงที่เล่นผ่านแท็ก <audio>

var audioElement = document.querySelector('audio');
var mediaSourceNode = context.createMediaElementSource(audioElement);
// Create the filter
var filter = context.createBiquadFilter();
// Create the audio graph.
mediaSourceNode.connect(filter);
filter.connect(context.destination);

สำหรับการพูดคุยที่สมบูรณ์เพิ่มเติมเกี่ยวกับการผสานรวมแท็ก <audio> กับ Web Audio API โปรดดูบทความสั้นๆ นี้

เอฟเฟกต์เสียง

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

คุณลักษณะสำคัญอีกอย่างของเอฟเฟกต์เสียงในเกมคือ อาจมีเอฟเฟกต์เสียงหลายเสียงได้พร้อมกัน สมมติว่าคุณอยู่ในการดวลปืนที่มีนักแสดงหลายคนกำลังยิงปืนกล ปืนกลแต่ละปืนยิงหลายครั้งต่อวินาที ทำให้เล่นเอฟเฟกต์เสียงหลายสิบครั้งพร้อมกัน การเล่นเสียงจากแหล่งที่มาที่มีกำหนดเวลาอย่างแม่นยำหลายแหล่งในเวลาเดียวกัน เป็นจุดเดียวที่ Web Audio API มีประสิทธิภาพมาก

ตัวอย่างต่อไปนี้สร้างรอบปืนกลจากตัวอย่างกระสุนเดี่ยวหลายๆ ตัว โดยการสร้างแหล่งที่มาของเสียงหลายแหล่งซึ่งเล่นโดยสับเปลี่ยนไปมาตามกาลเวลา

var time = context.currentTime;
for (var i = 0; i < rounds; i++) {
    var source = this.makeSource(this.buffers[M4A1]);
    source.noteOn(time + i - interval);
}

คราวนี้ ถ้าเสียงปืนกลทั้งหมดในเกมของคุณ น่าจะฟังดูน่าเบื่อ แน่นอนว่าค่าเหล่านี้จะแตกต่างกันไปด้วยเสียงตามระยะทางจากตำแหน่งและตำแหน่งที่สัมพันธ์กัน (ข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ในภายหลัง) แต่อาจไม่เพียงพอ โชคดีที่ Web Audio API ช่วยให้คุณสามารถปรับแต่งตัวอย่างด้านบนได้ 2 วิธี ดังนี้

  1. มีการเปลี่ยนเวลาเล็กน้อยระหว่างการยิงปืน
  2. ด้วยการปรับเปลี่ยนอัตราการเล่นของแต่ละตัวอย่าง (รวมถึงเปลี่ยนระดับเสียงสูงต่ำด้วย) เพื่อจำลองการสุ่มเลือกสิ่งต่างๆ ในโลกจริง

ดูตัวอย่างจริงของเทคนิคเหล่านี้ในการใช้งานจริงได้ที่การสาธิตการใช้โต๊ะพูล ซึ่งใช้การสุ่มตัวอย่างแบบสุ่มและอัตราการเล่นที่แตกต่างกันไปเพื่อให้ได้เสียงชนบอลที่น่าสนใจยิ่งขึ้น

เสียงตามตำแหน่งแบบ 3 มิติ

เกมมักมีฉากเป็นโลกที่มีสมบัติทางเรขาคณิตบางอย่าง อาจเป็น 2 มิติหรือ 3 มิติก็ได้ ในกรณีนี้ เสียงแบบสเตอริโอจะเพิ่มความสมจริงให้ประสบการณ์ได้อย่างมาก โชคดีที่ Web Audio API มาพร้อมกับฟีเจอร์เสียงตามตำแหน่งที่มีการเร่งฮาร์ดแวร์ในตัว ซึ่งใช้งานได้ทันที ลืมบอกไปว่าคุณมีลำโพงสเตอริโอ (แนะนำให้ใช้หูฟัง) สำหรับตัวอย่างต่อไปนี้ก็น่าจะเหมาะสมนะ

ในตัวอย่างข้างต้นจะมี Listener (ไอคอนบุคคล) อยู่ตรงกลางของ Canvas และเมาส์จะมีผลกับตำแหน่งของแหล่งที่มา (ไอคอนลำโพง) ตัวอย่างด้านบนเป็นตัวอย่างง่ายๆ ของการใช้ AudioPannerNode เพื่อให้ได้ผลลัพธ์ในลักษณะนี้ แนวคิดพื้นฐานของตัวอย่างข้างต้นคือเพื่อตอบสนองต่อการเคลื่อนที่ของเมาส์โดยการตั้งค่าตำแหน่งแหล่งที่มาของเสียง ดังนี้

PositionSample.prototype.changePosition = function(position) {
    // Position coordinates are in normalized canvas coordinates
    // with -0.5 < x, y < 0.5
    if (position) {
    if (!this.isPlaying) {
        this.play();
    }
    var mul = 2;
    var x = position.x / this.size.width;
    var y = -position.y / this.size.height;
    this.panner.setPosition(x - mul, y - mul, -0.5);
    } else {
    this.stop();
    }
};

สิ่งที่ควรทราบเกี่ยวกับการรักษาสภาพแวดล้อมของ Web Audio มีดังนี้

  • โดยค่าเริ่มต้น Listener จะอยู่ที่ต้นทาง (0, 0, 0)
  • API ตำแหน่งของ Web Audio เป็นแบบไร้หน่วย ผมเลยเปิดตัวตัวคูณเพื่อทำให้เสียงสาธิตดีขึ้น
  • Web Audio ใช้พิกัดแบบ Y-is-up (ตรงข้ามกับระบบกราฟิกของคอมพิวเตอร์ส่วนใหญ่) ซึ่งเป็นเหตุผลที่ผมสลับแกน Y ในตัวอย่างด้านบน

ขั้นสูง: เสียงกรวย

โมเดลตำแหน่งมีประสิทธิภาพมากและค่อนข้างขั้นสูงโดยยึดตาม OpenAL เป็นหลัก ดูรายละเอียดเพิ่มเติมได้ที่ส่วนที่ 3 และ 4 ของข้อกำหนดที่ลิงก์ไว้ด้านบน

รูปแบบตำแหน่ง

มี AudioListener รายการเดียวที่แนบมากับบริบท Web Audio API ซึ่งสามารถกำหนดค่าในพื้นที่ทํางานผ่านตำแหน่งและการวางแนวได้ แหล่งที่มาแต่ละแหล่งสามารถส่งผ่าน AudioPannerNode ได้ ซึ่งจะกระจายเสียงอินพุต โหนดของแพนเนอร์มีตำแหน่งและการวางแนว รวมถึงโมเดลระยะทางและทิศทาง

โมเดลระยะทางจะระบุปริมาณของเกนตามระยะใกล้กับแหล่งที่มา ขณะที่โมเดลกำหนดทิศทางสามารถกำหนดค่าได้โดยระบุกรวยด้านในและกรวยด้านนอก ซึ่งจะกำหนดจำนวนของกำไร (ปกติเป็นค่าลบ) หาก Listener อยู่ภายในกรวยด้านใน ระหว่างกรวยด้านในและด้านนอก หรือด้านนอกกรวยด้านนอก

var panner = context.createPanner();
panner.coneOuterGain = 0.5;
panner.coneOuterAngle = 180;
panner.coneInnerAngle = 0;

แม้ว่าตัวอย่างของฉันจะเป็นแบบ 2 มิติ แต่โมเดลนี้ทำให้มิติข้อมูลที่ 3 เป็นแบบทั่วไปได้อย่างง่ายดาย ดูตัวอย่างของเสียงที่จัดตำแหน่งแบบ 3 มิติได้ในตัวอย่างตำแหน่งนี้ นอกจากตำแหน่งแล้ว โมเดลเสียง Web Audio ยังจะรวมอัตราความเร็วสำหรับการเลื่อน Doppler ด้วย ตัวอย่างนี้แสดงเอฟเฟกต์ Doppler อย่างละเอียด

โปรดอ่านบทแนะนำโดยละเอียดนี้เพิ่มเติมใน [การมิกซ์เสียงตามตำแหน่งและ WebGL][webgl] เพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับหัวข้อนี้

เอฟเฟกต์และฟิลเตอร์ในห้อง

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

ถ้าจะพูดกว้างๆ ก็คือการตอบสนองที่เฉียบคม การตอบสนองแบบฉับพลันเหล่านี้อาจบันทึกไว้อย่างละเอียด และจริงๆ แล้วมีเว็บไซต์ที่โฮสต์ไฟล์การตอบสนองแบบ Impulse ที่บันทึกไว้ล่วงหน้า (เก็บไว้เป็นเสียง) จำนวนมากเพื่อความสะดวกของคุณ

สำหรับข้อมูลเพิ่มเติมมากมายเกี่ยวกับวิธีสร้างการตอบสนองแบบกระตุ้นจากสภาพแวดล้อมหนึ่งๆ โปรดอ่านส่วน "การตั้งค่าการบันทึก" ในส่วน Convolution ของข้อกำหนด API ของ Web Audio

ที่สำคัญกว่านั้นคือเพื่อวัตถุประสงค์ของเรา Web Audio API ช่วยให้นำการตอบสนองที่แรงกระตุ้นเหล่านี้ไปใช้กับเสียงของเราได้อย่างง่ายดายโดยใช้ ConvolverNode

// Make a source node for the sample.
var source = context.createBufferSource();
source.buffer = this.buffer;
// Make a convolver node for the impulse response.
var convolver = context.createConvolver();
convolver.buffer = this.impulseResponseBuffer;
// Connect the graph.
source.connect(convolver);
convolver.connect(context.destination);

ดูการสาธิตเอฟเฟกต์ห้องนี้ในหน้าข้อมูลจำเพาะของ Web Audio API รวมถึงตัวอย่างนี้ที่ให้คุณควบคุมทั้งความแห้ง (ดิบ) และเปียก (ประมวลผลผ่าน Convolver) ที่ผสมผสานมาตรฐานแจ๊สที่ยอดเยี่ยมได้

การนับถอยหลังครั้งสุดท้าย

คุณได้สร้างเกม กำหนดค่าเสียงตามตำแหน่งแล้ว และตอนนี้คุณก็มี AudioNode จำนวนมากในกราฟ โดยทั้งหมดจะเล่นพร้อมกัน เยี่ยมเลย แต่มีอีก 1 สิ่งที่ควรพิจารณา

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

เสียงขาดๆ หายๆ

ต่อไปนี้เป็นตัวอย่างจริงของการตัดคลิปในสถานการณ์จริง Waveform ที่ดูแย่:

เสียงขาดๆ หายๆ

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

ตรวจหาการตัด

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

// Assume entire sound output is being piped through the mix node.
var meter = context.createJavaScriptNode(2048, 1, 1);
meter.onaudioprocess = processAudio;
mix.connect(meter);
meter.connect(context.destination);

และตรวจหาการตัดคลิปได้ในเครื่องจัดการ processAudio ต่อไปนี้

function processAudio(e) {
    var buffer = e.inputBuffer.getChannelData(0);

    var isClipping = false;
    // Iterate through buffer to check if any of the |values| exceeds 1.
    for (var i = 0; i < buffer.length; i++) {
    var absValue = Math.abs(buffer[i]);
    if (absValue >= 1) {
        isClipping = true;
        break;
    }
    }
}

โดยทั่วไปแล้ว ให้ระวังอย่าใช้ JavaScriptAudioNode มากเกินไปเพื่อเหตุผลด้านประสิทธิภาพ ในกรณีนี้ การใช้การวัดแบบอื่นจะสำรวจ RealtimeAnalyserNode ในกราฟเสียงสำหรับ getByteFrequencyData ในเวลาที่แสดงผลตามที่ requestAnimationFrame กำหนด วิธีนี้มีประสิทธิภาพมากกว่าแต่มักจะไม่ได้รับสัญญาณส่วนใหญ่ (รวมถึงตำแหน่งที่อาจมีการคลิป) เนื่องจากการแสดงผลจะเกิดขึ้นมากที่สุดที่ 60 ครั้งต่อวินาที ในขณะที่สัญญาณเสียงเปลี่ยนแปลงเร็วกว่ามาก

เนื่องจากการตรวจหาคลิปมีความสำคัญมาก จึงมีแนวโน้มที่เราจะเห็นโหนด MeterNode Web Audio API ในตัวในอนาคต

ป้องกันการคลิป

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

เพิ่มน้ำตาลเล็กน้อย

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

การใช้การบีบอัดแบบ Dynamics เป็นความคิดที่ดี โดยเฉพาะอย่างยิ่งในสภาพแวดล้อมในเกม ซึ่งเป็นที่ที่คุณไม่รู้ว่าจะมีเสียงแบบไหนและใช้เมื่อใด Plink จากห้องทดลอง DinahMoe เป็นตัวอย่างที่ดี เพราะเสียงที่เล่นจะขึ้นอยู่กับคุณและผู้เข้าร่วมคนอื่นๆ คอมเพรสเซอร์จะมีประโยชน์ในเกือบทุกกรณี ยกเว้นบางกรณีที่พบไม่บ่อยนัก ซึ่งคุณต้องใช้แทร็กมายากลที่มีการปรับแต่งให้เสียง "เหมาะสม" อยู่แล้ว

การติดตั้งใช้งานนี้เป็นเพียงการใส่ DynamicsCompressorNode ในกราฟเสียงของคุณ ซึ่งโดยทั่วไปจะเป็นโหนดสุดท้ายก่อนปลายทาง

// Assume the output is all going through the mix node.
var compressor = context.createDynamicsCompressor();
mix.connect(compressor);
compressor.connect(context.destination);

บทความ Wikipedia นี้ให้ข้อมูลที่เป็นประโยชน์มากหากต้องการดูรายละเอียดเพิ่มเติมเกี่ยวกับการบีบอัดเกี่ยวกับไดนามิก

ในการสรุป ให้ฟังการตัดคลิปอย่างละเอียดและป้องกันการทดลองโดยแทรกโหนดเกนหลัก จากนั้นทำให้การมิกซ์ทั้งหมดแน่นขึ้นโดยใช้โหนดคอมเพรสเซอร์ Dynamic กราฟเสียงของคุณอาจมีลักษณะดังนี้

ผลหลังแข่งขันเสร็จสิ้น

บทสรุป

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

ดูข้อมูลเพิ่มเติมเกี่ยวกับ Web Audio ได้ในบทความเริ่มต้นใช้งานบทนำเพิ่มเติม และหากมีข้อสงสัย โปรดดูว่ามีคำตอบแล้วหรือยังในคำถามที่พบบ่อยเกี่ยวกับเสียงในเว็บ สุดท้าย หากมีข้อสงสัยเพิ่มเติม โปรดสอบถามใน Stack Overflow โดยใช้แท็ก web-audio

ก่อนจะไปลงชื่อเข้าใช้ เราขอฝากให้คุณใช้ Web Audio API ที่ยอดเยี่ยมในเกมจริงๆ วันนี้