Callbacks
ส่วนนี้ครอบคลุมการรวบรวมข้อมูล การสร้างฟังก์ชันเอฟเฟกต์ และการใช้คลาส Meter เพื่อเรียกใช้ฟังก์ชันเหล่านั้น เราจะไม่เน้นอธิบายเรื่องแอนิเมชันหรือรายละเอียดของตัวจัดการเอฟเฟกต์ ดังนั้นหากคุณต้องการทบทวน แนะนำให้ดูส่วน บทช่วยสอน หรือ เอกสารอ้างอิง API ของเรา
ขั้นตอนในการเรียกใช้แอนิเมชันอย่างมีประสิทธิภาพตั้งแต่ต้นจนจบมีดังนี้:
- ใช้เมธอด Meter.setValue(value) ภายในฟังก์ชัน update เพื่อใส่ข้อมูลมิเตอร์ดิบลงในอินสแตนซ์ของคลาส Meter ของคุณ
- หาก Meter ประเมินว่าตัวเองมีความเสถียร ก็จะเรียกใช้ฟังก์ชัน callback ที่ส่งเข้ามาเป็นพารามิเตอร์ตัวที่สอง
- ฟังก์ชัน callback นี้มีตรรกะเงื่อนไขเพื่อประเมินสถานะของ Meter หากสถานะเป็นจริง (truthy) ควรเพิ่มเอฟเฟกต์ของคุณลงในอาร์เรย์ effects (effects.push(new yourEffect())) หรือตัวจัดการสถานะ (steMgr.Push(new yourEffect()))
- การทำงานของฟังก์ชัน update แต่ละครั้งจะประเมินอาร์เรย์ effects ทั้งหมดและสมาชิกล่าสุดในตัวจัดการสถานะ ในขั้นนี้ เอฟเฟกต์จะถูกวาดตามตัวแปรสถานะ
- หลังจากวาดแล้ว แต่ละเอฟเฟกต์จะมีส่วนในการตรวจสอบอายุการทำงานของตัวเอง เอฟเฟกต์ในอาร์เรย์ effects เพียงลดค่าตัวแปรอายุการทำงานที่ผูกกับอินสแตนซ์ของคลาส เมื่ออายุการทำงานเป็น 0 หรือน้อยกว่า ก็สามารถลบเอฟเฟกต์ออกได้ง่าย ๆ ด้วย splice ระหว่างการประเมินอาร์เรย์ effects ส่วนเอฟเฟกต์ในตัวจัดการสถานะสามารถลบตัวเองออกได้เมื่อจำเป็น เนื่องจากตัวจัดการสถานะทำงานเป็นสแตก และตรรกะเงื่อนไขของเอฟเฟกต์เหล่านี้มักซับซ้อนกว่า
มิเตอร์
หัวข้อที่มีชื่อว่า “มิเตอร์”มิเตอร์นี้มองหาแถบพลังชีวิตทั่วไป และจะคืนค่าเปอร์เซ็นต์ของสีที่ตรงกันเป็นตัวเลขระหว่าง 0 ถึง 1 ในกรณีนี้ช่วง HSL ค่อนข้างกว้าง เพื่อรองรับการไล่ระดับสีของแถบ ซึ่งทำให้เกิดปัญหาขึ้น เมื่อแถบพลังชีวิตหดลง จะเผยให้เห็นพื้นหลังโปร่งใส ซึ่งอาจมีองค์ประกอบสีเขียวจากสภาพแวดล้อมในเกมที่ผ่านการตรวจสอบสีและทำให้มิเตอร์ของเราคลาดเคลื่อน
<head> <meta meter="health" tags="example" x= ".05" y=".9" width=".189" h="70-140" s="40-100" l="40-100" type="linear"/></head>ในที่นี้ เราได้สร้างอินสแตนซ์ของคลาส Meter ชื่อ healthM เพื่อเรียกใช้เอฟเฟกต์ “ได้รับความเสียหาย”
<script> // Meter instance var healthM = new Meter(25, healthHelper);
function update () => { healthM.setValue(health); window.requestAnimationFrame(update); }
function healthHelper () => { // "Took Damage" effect up next }
// . . .
</script>เราได้กำหนดค่าความเสถียรที่ต้องการไว้ค่อนข้างสูง (25) เพื่อรับมือกับปัญหาความไม่เสถียรข้างต้น ฟังก์ชัน callback ที่ผูกไว้จะถูกเรียกใช้ก็ต่อเมื่อค่ามิเตอร์คงที่ติดต่อกัน 25 ครั้งของการอัปเดต ซึ่งจะกรองข้อมูลส่วนใหญ่ที่ไม่ใช่แถบพลังชีวิตจริงออกไป แม้ผู้เล่นจะอยู่นิ่งก็ตาม ข้อเสียเพียงอย่างเดียวคือความหน่วงของเอฟเฟกต์ที่เพิ่มขึ้น ซึ่งจะเป็นปัจจัยจำกัดในสถานการณ์นี้เสมอ สำหรับแถบพลังชีวิตนี้โดยเฉพาะ เราสามารถแก้ไขได้อย่างน้อยสองวิธี:
- เมื่อถูกโจมตี ส่วนหนึ่งของแถบสีเขียวจะเปลี่ยนเป็นสีแดงทันทีก่อนจะหดลงสู่ระดับพลังชีวิตสีเขียวใหม่ หากเราเพิ่มมิเตอร์ healthRed ที่มองหาสีแดงในตำแหน่งเดียวกับมิเตอร์พลังชีวิต เราสามารถรวมค่าทั้งสองเพื่อให้เรียกใช้ได้เร็วขึ้น เงื่อนไข if ( healthM.decreased && healthRed.increased ) น่าจะให้ความแม่นยำที่ดีในกรณีนี้
- เรายังสามารถรวมค่าที่อ่านได้จากมิเตอร์แบบ linear หลายตัวที่มีการตั้งค่าสีเดียวกันได้ ขณะนี้เรามีมิเตอร์แบบ linear หนึ่งตัวตั้งไว้ตรงกลางแถบพลังชีวิต หากตั้งเพิ่มอีกสองตัว ตัวหนึ่งอยู่เหนือและอีกตัวอยู่ใต้ตัวเดิมเล็กน้อยแต่ยังอยู่ภายในแถบพลังชีวิต ความใกล้เคียงกันของค่ามิเตอร์ทั้งสามสามารถใช้เป็นการตรวจสอบความเสถียรเพิ่มเติมได้ มิเตอร์ทั้งสามตัวต้องอ่านค่า Meter.decreased และมีค่าอยู่ในช่วงใกล้เคียงกันจึงจะเรียกใช้เอฟเฟกต์
Callback
หัวข้อที่มีชื่อว่า “Callback”ฟังก์ชัน callback นี้จะทำงานหลังจากมิเตอร์ยืนยันแล้วว่าสมาชิกทั้งหมดในอาร์เรย์ค่าของมันเท่ากัน ซึ่งช่วยให้มั่นใจว่าค่ามิเตอร์มาจากข้อมูลที่เสถียร ภายใน callback เราจะใช้คำสั่งเงื่อนไขเพื่อตัดสินว่าควรเล่นเอฟเฟกต์หรือไม่
let healthPrev = 0;
function healthHelper () => { if (healthM.decreased && healthM.value != healthPrev){ effects.push(new healthEffect()); healthPrev = healthM.value; }}คำสั่งเงื่อนไขนี้ต้องการเงื่อนไขที่เป็นจริงสองข้อ ได้แก่ การลดลงของมิเตอร์อย่างเสถียร และค่ามิเตอร์ปัจจุบันไม่ตรงกับค่าที่บันทึกไว้ก่อนหน้า เราเพิ่มเงื่อนไขที่สองนี้เพื่อหลีกเลี่ยงบั๊ก หากเราตรวจสอบเพียงการลดลง เอฟเฟกต์จะเล่นซ้ำไปเรื่อย ๆ เนื่องจาก Meter.decreased ถูกตั้งค่าเมื่อมิเตอร์เสถียรแล้ว ค่าของมันจะคงเดิมตราบใดที่มิเตอร์ไม่เปลี่ยนแปลง ด้วยวิธีนี้ เอฟเฟกต์จะเล่นก็ต่อเมื่อมีการลดลงที่ต่างจากค่าเสถียรครั้งล่าสุดเท่านั้น
เอฟเฟกต์
หัวข้อที่มีชื่อว่า “เอฟเฟกต์”เราจะสร้างเอฟเฟกต์ให้คุณสองแบบ: แบบหนึ่งสำหรับอาร์เรย์ effects และอีกแบบสำหรับตัวจัดการสถานะ
อาร์เรย์ Effects
หัวข้อที่มีชื่อว่า “อาร์เรย์ Effects”โค้ดบล็อกแรกนี้กำหนดฟังก์ชันเอฟเฟกต์สำหรับอาร์เรย์ effects ซึ่งออกแบบมาให้รวดเร็วและเบา เพื่อให้เอฟเฟกต์ย่อยหลายตัวทำงานพร้อมกันได้ เพื่อลดภาระของระบบ เรายังทำให้เอฟเฟกต์ปรับแต่งได้ง่ายผ่านพารามิเตอร์เพื่อนำกลับมาใช้ซ้ำได้
function healthEffect(color, direction, speed){ // The lifetime of the effect should be iterated in the draw function. this.lifetime = 200; this.speed = speed; this.direction = direction; this.color = color;
this.draw = () => { // Set fill from parameter ctx.fillStyle = `hsl(${this.color}, 100%, 50%)`; // Check direction if (this.direction == "up"){ ctx.fillRect(0, this.lifetime, width, 30); } else { ctx.fillRect(0, height - this.lifetime, width, 30); } // As the lifetime decreases by 5 each update, the rectangle moves accordingly. this.lifetime -= speed; } }function update() { // Background color ctx.fillStyle = "black"; ctx.fillRect(0, 0, 320, 200);
// Draw all effects in array and remove them if they are done. effects.forEach((ele, i) => { ele.draw(); if (ele.lifetime == 0){ effects.splice(i, 1); } })
window.requestAnimationFrame(update);}function healthHelper () => { if (healthM.decreased && healthM.value != healthPrev){ // Make sure to correctly set the color and direction paremeters. effects.push(new healthEffect(1, "down", 5)); healthPrev = healthM.value; }}เมื่อทำงานจริงควรมีลักษณะดังนี้:

ด้วยการออกแบบฟังก์ชันที่ปรับแก้ได้ เราจึงสร้างเอฟเฟกต์ฟื้นฟูพลังชีวิตได้ง่าย ๆ เช่นกัน! เพียงเพิ่มเงื่อนไขอีกข้อในฟังก์ชัน callback
function healthHelper () => { if (healthM.decreased && healthM.value != healthPrev){ // Damage effect effects.push(new healthEffect(1, "down", 5)); healthPrev = healthM.value; } if (healthM.increased && healthM.value != healthPrev){ // Healing effect effects.push(new healthEffect(120, "up", 5)); healthPrev = healthM.value; }}
ความเรียบง่ายของเอฟเฟกต์นี้คือข้อได้เปรียบที่ใหญ่ที่สุดสำหรับนักพัฒนา LightScript แทนที่จะสร้างเอฟเฟกต์ที่ออกแบบเฉพาะซึ่งแต่ละตัวต้องใช้โค้ดหลายร้อยบรรทัด ให้สร้างเอฟเฟกต์ขนาดเล็กที่นำกลับมาใช้ซ้ำได้ เพื่อเป็นส่วนประกอบพื้นฐานสำหรับทริกเกอร์เฉพาะต่าง ๆ เอฟเฟกต์สามแบบถัดไปเป็นรูปแบบที่ดัดแปลงจากเอฟเฟกต์เล็ก ๆ ที่เราเพิ่งสร้าง ลองทดลองดูได้ตามสบายว่าคุณจะสร้างอะไรได้บ้าง!



ตัวจัดการสถานะ
หัวข้อที่มีชื่อว่า “ตัวจัดการสถานะ”โดยส่วนตัวแล้ว เราสงวนตัวจัดการสถานะไว้สำหรับเอฟเฟกต์ขนาดใหญ่และซับซ้อนที่มีตรรกะสถานะเฉพาะเจาะจงมาก โดยเฉพาะเมื่อคุณต้องการให้เอฟเฟกต์นั้นมีลำดับความสำคัญเหนือเอฟเฟกต์อื่นทั้งหมด โปรดจำไว้ว่าตัวจัดการสถานะทำงานเหมือนสแตกและจะเรียกใช้เฉพาะเอฟเฟกต์ที่อยู่บนสุดเท่านั้น เอฟเฟกต์สถานะแต่ละตัวมีหน้าที่ลบตัวเองออกเมื่อทำงานเสร็จ ตัวอย่างนี้แสดงแอนิเมชันล็อบบี้จับคู่ผู้เล่น (matchmaking) ที่ปรับเปลี่ยนตามว่าคุณกำลังค้นหาอยู่หรือไม่ และยังมีอาร์เรย์ effects ขนาดเล็กของตัวเองพร้อมเอฟเฟกต์ตัวช่วยอีกด้วย มิเตอร์ที่ใช้ในที่นี้คือ inLobby และ yellowPlayButton ซึ่งจะทำงานเฉพาะในล็อบบี้จับคู่ผู้เล่นเท่านั้น yellowPlayButton จะเปลี่ยนสีขึ้นอยู่กับว่าคุณกำลังค้นหาอยู่หรือไม่
function lobbyAnimation(){ // Instance variables this.start = new Date().getTime(); this.elapsed = 0; this.radius = 0; this.searching = 1000; // Instance effects array this.effects = [];
// Process is the method that allows a state effect to remove itself this.Process = function () { // If the lobby menu and play button disappear, remove the effect if (inLobby.decreased && yellowPlayButton.decreased) { stateMgr.Pop(); // Global boolean to prevent duplicate lobby effects in the state handler lobbyAnim = false; } // If the play button is present, edit this.searching based on its color yellowPlayButton.value == 1 ? this.searching = 1000 : this.searching = 100; // Call the Draw function attached to the effect this.Draw(); };
this.Draw = function(){ // Iterate important variables this.elapsed = new Date().getTime() - this.start; this.radius = 50 + (Math.sin(this.elapsed/this.searching)*10);
// Fill the background to overwrite other effects ctx.fillStyle = 'black'; ctx.fillRect(0, 0, 320, 200);
// Draw the main arc ctx.beginPath(); ctx.strokeStyle = `hsl(120, 100%, 50%)`; ctx.lineWidth = 20; ctx.arc(160, 100, this.radius, 0, 2 * Math.PI); ctx.stroke(); // If the main arc is the right size, push helper effects into the helper array if(this.radius > 59 && this.elapsed % 2 == 0){ this.effects.push(new lobbyHelper(160, 100, 69)); } // Play and remove helper effects if necessary this.effects.forEach((e, i) => { ctx.lineWidth = 1; e.draw(); if (e.radius > 360) { this.effects.splice(i, 1); } }); } }
function lobbyHelper(x, y, radius){ this.x = x; this.y = y; this.radius = radius; this.draw = function(){ // Set the global transparency value. 1 is opaque, 0 is invisible. ctx.globalAlpha = 1 - this.radius/360; ctx.beginPath(); ctx.strokeStyle = `hsl(120, 100%, ${50 + (this.radius/7)}%)`; ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI); ctx.stroke(); // Set the global transparency back to 1 so your other effects are opaque ctx.globalAlpha = 1; this.radius++; }}// Declare state handler (found in Snippets)var stateMgr = new StateHandler();// Global boolean to prevent duplicate lobby animationsvar lobbyAnim = false;
function update(){ // Run the Process function to draw the animation stateMgr.Process();}function lobbyAnimationPlay(){ if(inLobby.value == 1 && yellowPlayButton.value == 1 && !lobbyAnim){ stateMgr.Push(new lobbyAnimation()); lobbyAnim = true; }};ไม่ได้ค้นหา:

กำลังค้นหา:

และนี่คือบทสรุปของบทช่วยสอน callbacks รวมถึงคู่มือการพัฒนาอย่างเป็นทางการของ SignalRGB โปรดติดตามส่วนถัดไปและส่วนสุดท้ายของคู่มือของเรา การดูแลรักษา Lightscript!