ทริกแอนิเมชันด้วย Exponential Smoothing
- มีเทคนิคแอนิเมชันง่าย ๆ อย่างหนึ่งที่ฉันใช้แทบตลอดตั้งแต่เริ่มทำงานที่เกี่ยวข้องกับแอนิเมชัน
- เทคนิคนี้ถูกนำไปใช้ได้หลากหลาย ไม่ว่าจะเป็นการหมุนและเลื่อนกล้อง การขยับตัวละครในเกมแบบเทิร์นเบส การเคลื่อนย้ายองค์ประกอบ UI หรือการทำให้การเปลี่ยนระดับเสียงในไลบรารีเสียงนุ่มนวลขึ้น
- เทคนิคนี้ไม่ใช่ของใหม่ และคุณอาจเคยได้ยินหรือเคยใช้มาก่อนแล้ว แต่ฉันจะอธิบายผ่านตัวอย่างบางส่วนและหลักการทางคณิตศาสตร์ของมัน
ปุ่มสลับ
- สมมติว่ากำลังสร้างองค์ประกอบ UI เช่น ปุ่มสลับ
- ตำแหน่งของสวิตช์ในปุ่มสลับจะคำนวณจากสถานะ โดยถ้าเปิดจะตั้งเป็น
max_x และถ้าปิดจะตั้งเป็น min_x
- วิธีนี้ใช้งานได้ดี แต่ถ้าไม่มีแอนิเมชันก็ดูขาดชีวิตชีวาไปหน่อย
- แอนิเมชันไม่ได้แค่เพิ่มความสวยงามทางสายตา แต่ยังช่วยให้ผู้ใช้เข้าใจด้วยว่ากำลังเกิดอะไรขึ้น
- แทนที่จะย้ายตัวบ่งชี้ของปุ่มสลับไปยังตำแหน่งใหม่ทันที ก็เปลี่ยนให้มันค่อย ๆ เคลื่อนไปอย่างนุ่มนวล
- ตอนนี้จึงต้องอัปเดตแอนิเมชัน ซึ่งมีข้อเสียคือมันดูเหมือนเคลื่อนที่ด้วยความเร็วคงที่
- จากนั้นสามารถเพิ่ม easing function เข้าไปได้ เช่น
3t^2-2t^3 หรือ sqrt(t)
- ความแตกต่างของ easing function เหล่านี้จะสังเกตเห็นได้ชัดขึ้นเมื่อเล่นแอนิเมชันช้าลง
- ตอนนี้แทนที่จะอัปเดตตำแหน่งของสวิตช์ ก็ต้องติดตามสถานะของแอนิเมชันแทน
- หากใช้
sqrt ก็ต้องระบุ easing function คนละตัวอย่างชัดเจนตามทิศทางของแอนิเมชัน
- อะไรจะดูดีที่สุดนั้นขึ้นอยู่กับรสนิยม แต่
sqrt ดูดีที่สุด เพราะสวิตช์เริ่มต้นได้เร็วมาก แต่จะชะลอลงอย่างเหมาะเจาะเมื่อเข้าใกล้ปลายทาง
- ข้อเสียของเวอร์ชันนี้คือ แม้แต่ในกรณีที่ง่ายที่สุดก็ยังต้องจัดการหลายอย่างพอสมควร และถ้าผู้ใช้คลิกกลางคันระหว่างแอนิเมชัน ก็จะเกิดความไม่ต่อเนื่องที่ดูเหมือนมันกระโดดทันที
การเลื่อนกล้อง
- สมมติว่ามีสถานการณ์ที่แผนที่และกล้องต้องเลื่อนหรือเคลื่อนที่ไปรอบ ๆ
- การเพิ่มแอนิเมชันตรงนี้ก็เป็นความคิดที่ดีเช่นกัน
- มีการยกตัวอย่างโค้ดที่ทำ interpolation ด้วยความเร็วคงที่
- อาการสั่นที่เกิดหลังแอนิเมชันเสร็จ มาจากการที่
target.x - position.x สลับไปมาระหว่างค่าบวกและค่าลบ
- สิ่งที่ต้องใช้คือฟังก์ชันสำหรับ clamp ค่า delta แทน
sign(delta)
- วิธีนี้ซับซ้อนพอสมควรสำหรับเรื่องที่ควรจะง่าย
- มันดูแปลกเมื่อความเร็วแอนิเมชันมากกว่าระยะจนทำให้ไปถึงก่อนแอนิเมชันควรจบ
- จะเลือกเพิกเฉยต่ออินพุตของผู้ใช้ขณะแอนิเมชันทำงานอยู่ก็ได้ แต่สิ่งนี้สร้างประสบการณ์ที่น่าหงุดหงิดมากให้ผู้ใช้
- แนวทางที่สมบูรณ์แบบก็คือ Exponential Smoothing นั่นเอง
- เมื่อเทียบกับตัวอย่างปุ่มสลับแล้ว โค้ดแทบไม่ต่างกันเลย
กลไกภายใน
- อธิบายว่า
1 - exp(- speed * dt) คืออะไร และมันทำงานอย่างไร
- เริ่มจากเวอร์ชันง่าย ๆ ก่อน โดยทำให้ความเร็วในการเคลื่อนที่เป็นสัดส่วนกับระยะห่างระหว่าง
position ปัจจุบันกับตำแหน่งใหม่ target ที่ต้องไปให้ถึง
- วิธีนี้ไม่ต้องเก็บสถานะใด ๆ เพิ่มเติมนอกจากตำแหน่งปัจจุบันและตำแหน่งเป้าหมาย และยังปรับตัวอัตโนมัติได้แม้
target จะเปลี่ยนกะทันหัน
- แต่มีปัญหาเล็กน้อยอยู่ ถ้า
speed * dt มากกว่า 1 ตำแหน่งจะเคลื่อนเลยเป้าหมาย
- เพื่อแก้ปัญหานี้ สามารถ clamp ค่าไว้ที่ 1 ได้
- สาเหตุที่
speed * dt ใหญ่เกินไป อาจเป็นเพราะค่า speed สูงเกินไปหรือ dt ใหญ่เกินไป
- สำหรับแอนิเมชัน คงจะดีถ้าทุกอย่างทำงานได้สมบูรณ์เมื่อใส่
dt เข้าไป
สมการเชิงอนุพันธ์ (โอ้ ไม่นะ)
- มีการนำเสนอแนวทางสองขั้นตอนเพื่อแก้ปัญหานี้
- ที่
position += (target - position) * speed * dt ใช้ได้กับ dt ที่เล็ก แต่ล้มเหลวกับ dt ที่ใหญ่ เป็นปัญหาคลาสสิกของวิธีเชิงตัวเลขในการแก้สมการเชิงอนุพันธ์
- จากนั้นดูว่าสมการนี้กำลังแก้อะไรอยู่
- และอธิบายว่า
position += (target - position) * (1 - exp(- speed * dt)) คือสูตรที่ถูกต้องสำหรับทุกค่า dt
การเลือกความเร็ว
- โดยทั่วไปเรามักคิดถึงแอนิเมชันในแง่ของระยะเวลา
- แต่เมื่อใช้สูตรแบบเอ็กซ์โปเนนเชียล แอนิเมชันจะใช้เวลาจบอย่างเป็นอนันต์ในเชิงเทคนิค
- ความหมายของพารามิเตอร์
speed คือ 1 / speed จะเป็นเวลาที่ position เข้าใกล้ target มากขึ้นอีก e = 2.71828... เท่า
Exponential Smoothing
- ถ้าค้นหาคำว่า "Exponential Smoothing" คุณอาจเจอบทความวิกิที่ดูเหมือนไม่เกี่ยวกันเลย แต่จริง ๆ แล้วมันมีสูตรที่คล้ายกับสิ่งที่โพสต์นี้พูดถึงมาก
- หากสมมติว่า
dt คงที่เสมอ และ target เปลี่ยนทุกครั้งในแต่ละรอบ ก็สามารถจัดทำดัชนีค่าตามลำดับรอบ และคำนวณบางอย่างอย่าง position[i] = (target[i] - position[i - 1]) * factor
ชื่อย่อหน้าสุดท้าย
- ฉันมีไอเดียสำหรับโพสต์นี้มาหลายเดือนแล้ว และก็ดีใจที่ในที่สุดทำมันเสร็จ
- ขอบคุณที่รับชมและอ่าน devlog
ความเห็นของ GN⁺
- บทความนี้อธิบายเทคนิค Exponential Smoothing ที่ใช้ทำให้แอนิเมชันลื่นไหลและเป็นธรรมชาติมากขึ้น เทคนิคนี้ช่วยยกระดับประสบการณ์ผู้ใช้และทำให้อินเทอร์เฟซใช้งานได้อย่างเป็นธรรมชาติมากขึ้น
- Exponential Smoothing ยังมีประโยชน์ในการจำลองการเคลื่อนไหวทางกายภาพ เช่น ในการพัฒนาเกมที่ต้องการให้การเคลื่อนที่ของตัวละครหรือการเคลื่อนกล้องดูเป็นธรรมชาติมากขึ้น
- เทคนิคนี้มีประสิทธิภาพมากเป็นพิเศษเมื่อต้องแสดงผลการเปลี่ยนสถานะขององค์ประกอบในส่วนติดต่อผู้ใช้ในเชิงภาพ ตัวอย่างเช่น สามารถทำให้การเคลื่อนไหวของสไลเดอร์หรือสวิตช์ดูนุ่มนวลขึ้นได้
- หากมองในเชิงวิพากษ์ Exponential Smoothing อาจควบคุมความเร็วและระยะเวลาของแอนิเมชันได้ยากอย่างแม่นยำ ซึ่งอาจเป็นข้อจำกัดเมื่อดีไซเนอร์ต้องการปรับแต่งเอฟเฟกต์แอนิเมชันแบบละเอียดเฉพาะจุด
- ไลบรารีหรือเฟรมเวิร์กแอนิเมชันอื่นที่ให้ความสามารถคล้ายกัน เช่น GreenSock Animation Platform(GSAP) หรือ anime.js ซึ่งให้การควบคุมแอนิเมชันที่ละเอียดกว่า พร้อม easing function ที่หลากหลาย
- เมื่อนำเทคนิค Exponential Smoothing มาใช้ จำเป็นต้องหาสมดุลระหว่างความเป็นธรรมชาติของแอนิเมชันกับความแม่นยำในการควบคุม ข้อดีของการเลือกใช้เทคนิคนี้คือประสบการณ์ผู้ใช้ที่ดีขึ้น ส่วนข้อเสียคืออาจปรับจังหวะเวลาแอนิเมชันแบบละเอียดได้ยาก
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
สรุปความคิดเห็นแรก:
smoothstep()แต่เป็นวิธีแบบไร้สถานะ (stateless) ที่จัดการอินพุตหลากหลายแบบได้อย่างสม่ำเสมอสรุปความคิดเห็นที่สอง:
สรุปความคิดเห็นที่สาม:
สรุปความคิดเห็นที่สี่:
สรุปความคิดเห็นที่ห้า:
สรุปความคิดเห็นที่หก:
สรุปความคิดเห็นที่เจ็ด:
สรุปความคิดเห็นที่แปด:
สรุปความคิดเห็นที่เก้า:
สรุปความคิดเห็นที่สิบ: