จากลาไปหลังพัฒนาเกมด้วย Rust มา 3 ปี
(loglog.games)- ต่อข้ออ้างที่ว่าเมื่อคุ้นเคยกับ Rust แล้วปัญหาทุกอย่างจะหายไป
- ถึงจะคุ้นกับ Rust มากขึ้น ปัญหาเชิงรากฐานก็ไม่ได้หายไป
- เกมคือ state machine ที่ซับซ้อนและความต้องการเปลี่ยนตลอด จึงไม่เข้ากับธรรมชาติของ Rust ที่ตายตัวและตรวจสอบเข้มงวดเกินไป
- ปัญหาที่ต้องรีแฟกเตอร์โค้ดอยู่ตลอดเป็นสิ่งที่สร้างขึ้นมาเอง
- ปัญหาการรีแฟกเตอร์ครั้งใหญ่ที่เกิดจาก borrow checker
- Rust บังคับให้ต้องรีแฟกเตอร์บ่อยกว่าภาษาอื่น
- เมื่อความต้องการของเกมเปลี่ยนบ่อย ก็ต้องคอยสู้กับ borrow checker และจำเป็นต้องปรับโครงสร้างโค้ดใหม่
- ยากจะเห็นด้วยกับข้ออ้างที่ว่าการรีแฟกเตอร์จะทำให้ได้โค้ดที่ดี เพราะโค้ดที่ดีเกิดจากการวนซ้ำไอเดียและลองผิดลองถูก
- การทำให้มีชั้นอ้อม (Indirection) แก้ปัญหาได้แค่บางส่วนและทำให้ประสิทธิภาพการพัฒนาลดลง
- หากใช้ indirection เพื่อแก้ปัญหา borrow checker ตรรกะของโค้ดจะถูกแยกออกและซับซ้อนขึ้น
- ในเกมมีทั้งเหตุการณ์ที่เชื่อมโยงกัน จังหวะเวลาเฉพาะ และการจัดการสถานะจำนวนมาก ซึ่ง indirection ทำให้สิ่งเหล่านี้ยากขึ้น
- ใน Rust แม้แต่โค้ดง่าย ๆ ก็ต้องเขียนยืดยาวเพื่อรองรับ indirection
- ECS(Entity-Component-System) กำลังแก้ปัญหาผิดจุด
- ข้อดีของ ECS ที่แท้จริงคือข้อดีของ generational arena
- มอง ECS ได้จากหลายมุม เช่น dynamic composition, structure of arrays, วิธีแก้ปัญหา Rust borrow checker, dynamically created generational arenas
- แต่ก็ยังน่าสงสัยว่า ECS คือวิธีที่เหมาะสมที่สุดสำหรับการพัฒนาเกมหรือไม่ เพราะควรใส่ใจกับตรรกะของเกมมากกว่าประสิทธิภาพหรือ composition
- ระบบแบบทำให้เป็นทั่วไป (Generalized systems) ไม่ได้นำไปสู่เกมเพลย์ที่สนุก
- หากใช้ระบบที่ทั่วไปเกินไป เกมเพลย์ที่ได้มักน่าเบื่อ
- เกมที่ดีต้องออกแบบปฏิสัมพันธ์เชิงรายละเอียดอย่างระมัดระวัง ซิงก์ VFX ทำ playtest และทดลองซ้ำ ๆ รวมถึงปล่อยให้เร็วเพื่อรับฟีดแบ็ก
- Rust ยึดถือคุณค่าที่ไม่เหมาะกับการทำต้นแบบอย่างรวดเร็วและการวนซ้ำ (iteration)
- สิ่งสำคัญของการพัฒนาเกมคือการทำต้นแบบและวนซ้ำอย่างรวดเร็ว แต่คุณค่าของ Rust กลับอยู่คนละด้าน
- แก่นของการพัฒนาเกมคือเกมและประสบการณ์การเล่น ไม่ใช่โค้ดที่ไม่มีวันแครช
- ความสามารถในการบำรุงรักษาโค้ดเกมไม่ใช่คุณค่าที่สำคัญมากนักสำหรับเกมอินดี้ สิ่งสำคัญคือความเร็วในการวนซ้ำ (iteration speed)
- Rust หมกมุ่นกับการหลีกเลี่ยงปัญหาจนพลาดสิ่งที่สำคัญกว่า
- Procedural macros ยังห่างไกลจาก reflection มาก
- ในการพัฒนาเกมต้องเขียนโค้ดหลายด้าน ทั้งโค้ดระบบ โค้ดเกมเพลย์ UI VFX เสียง และเครื่องมือ
- ใน Rust แม้แต่การ "แสดงผลออบเจ็กต์" แบบง่าย ๆ ก็ต้องเขียนโค้ดเองหรือสร้าง procedural macros
- Procedural macros มีข้อจำกัดมากกว่า declarative macros มาก และยังใช้เวลาคอมไพล์นานกว่า
- Reflection ของ C# ใช้งานง่ายมาก และช่วยให้พัฒนาได้เร็วเมื่อประสิทธิภาพไม่ใช่ประเด็นสำคัญ
- Hot reloading มีบทบาทสำคัญต่อการเพิ่มความเร็วในการวนซ้ำ
- Hot reloading มีประโยชน์มากกับ immediate mode UI/การวาดภาพ การดีบัก และการปรับจูนค่าคงที่ของเกมเพลย์
- Rust ก็มี
hot-lib-reloaderแต่ยังไม่สมบูรณ์ และต้องอาศัยการวางแผนกับการมองล่วงหน้า จึงจำกัดการใช้งานเชิงสร้างสรรค์ - นักพัฒนาเกมต้องการ tooling ที่อยู่ในระดับสูงกว่าการรีโหลด struct แบบธรรมดา
- Abstraction ไม่ใช่ทางเลือก แต่เป็นสิ่งจำเป็น
- ตัวอย่างจริง) หากต้องให้ทำงานต่างกันตามสถานะของ UI ใน Rust อาจต้องรีแฟกเตอร์หรือไม่ก็ clone อย่างหนัก
- หลายครั้งไม่ได้เปลี่ยน business logic แต่เปลี่ยนโค้ดเพื่อทำให้คอมไพเลอร์พอใจ
- Rust ไม่มีระบบชนิดแบบโครงสร้าง เช่น "ชนิดที่มีฟิลด์เหล่านี้" จึงต้องแก้โค้ดหลายจุดเวลารีแฟกเตอร์
- สถานการณ์ GUI ของ Rust แย่มาก
- สิ่งสำคัญในเกม UI ไม่ใช่ data binding หรือ reactive update แต่คือการปรับแต่งรูปลักษณ์
- สิ่งที่เกม UI ต้องการคือ GUI ที่สวยงาม สไปรต์ที่ปรับแต่งได้ แอนิเมชัน vector shape อนุภาค เอฟเฟกต์ และแฟลช
- ตอนนี้ยังไม่มีไลบรารี Rust ที่ออกแบบมาเฉพาะสำหรับเกม GUI
- Orphan rule ควรเป็นตัวเลือกที่ปิดได้
- Orphan rule ลดประสิทธิภาพการพัฒนาอย่างมากเพื่อแลกกับความปลอดภัย
- ในโค้ดแอปพลิเคชันที่ไม่ใช่ไลบรารี ควรสามารถปิด orphan rule ได้
- เวลาคอมไพล์ดีขึ้นแล้ว แต่ถ้าใช้ proc macros ก็ยังช้าอยู่
- Proc macros อย่าง
serdeทำให้เวลาคอมไพล์เพิ่มขึ้นมาก - หาก incremental build ใช้เวลาเกิน 20-30 วินาที จะปรับจูนรายละเอียดได้ยากมาก
- Proc macros อย่าง
- ระบบนิเวศการพัฒนาเกมด้วย Rust ให้ความรู้สึกว่าถูกโหมเกินจริง
- คนที่กำลังทำเกมจริง ๆ มีไม่มาก
- หลายโปรเจกต์ที่หน้าเว็บหรือ README ดูโดดเด่นและมีชื่อเสียง แท้จริงแล้วอาจไม่เป็นเช่นนั้น
- ถ้าอยากทำเกมจริง แนะนำ
godot-rustอย่ายึดติดกับโซลูชัน Rust ล้วน ๆ และควรอาศัยเอนจินที่โตเต็มที่แล้ว
- เกมเป็น single-threaded ดังนั้นเหตุผลที่บอกว่าสถานะ global ไม่สะดวกจึงไม่ถูกต้อง
- การใช้สถานะ global ใน Rust ทำได้ลำบากมาก (
static mut,AtomicRefCell,Rcเป็นต้น) - แต่ต่างจากบริการแบ็กเอนด์ เกมส่วนใหญ่เป็น single-threaded ทั้งหมด จึงไม่จำเป็นต้องมีความลำบากแบบนี้
- มองว่าการที่ Bevy นำระบบขนานเข้ามาเป็นความผิดพลาด แม้ทำเพื่อประสิทธิภาพ แต่กลับเพิ่มความไม่สะดวก เช่น ผู้ใช้ต้องคอยระบุลำดับเองตลอด
- การใช้สถานะ global ใน Rust ทำได้ลำบากมาก (
- Dynamic borrow checking ทำให้เกิดแครชที่คาดไม่ถึงหลังการรีแฟกเตอร์
- จากการใช้
hecsมา 2 ปี พบกรณีที่ query ซ้อนทับกันจนเกิดแครชอยู่บ่อย - แม้ใช้
RefCellหากมี.borrow_mut()จากสองจุดก็เกิดแครชแบบไม่คาดคิดได้ - ปัญหาไม่ใช่ว่าโค้ดแย่ แต่เป็นเพราะ Rust บังคับให้ต้องรีแฟกเตอร์โดยไม่จำเป็น
RefCellมีประโยชน์ในเกม แต่ Rust ทำให้ใช้งานมันได้ยากเกินไป
- จากการใช้
- ความยืดหยุ่นของออบเจ็กต์ Context ยังไม่พอ
- เพราะใช้สถานะ global ได้ยาก ใน Rust จึงนิยมรวม reference ไว้ในออบเจ็กต์ context แล้วส่งต่อ
- แต่ถ้ายืมใช้เพียงบางฟิลด์ ก็อาจเกิด compile error จาก partial borrow
- แม้จะบอกให้แยก Context และเปลี่ยนโครงสร้าง แต่สิ่งที่อยากทำจริง ๆ คือ logic ของเกม การต้องเปลี่ยนโครงสร้างเพื่อเอาใจคอมไพเลอร์จึงเป็นการเสียเวลา
ข้อดีของ Rust
- ถ้าคอมไพล์ผ่านแล้ว โดยทั่วไปก็มักทำงานได้ดี โดยเฉพาะกับเครื่องมือ CLI การประมวลผลข้อมูล และการเขียนอัลกอริทึม
- ให้ประสิทธิภาพที่ดีได้โดยแทบไม่ต้องออกแรงมาก มีประสบการณ์ว่าเร็วกว่า C# ราว 1.5~2.5 เท่า
- Enum ถูกออกแบบมาได้ดี
- ด้วย Rust analyzer ทำให้ประสบการณ์ใช้งาน IDE ดีขึ้นมาก
- ระบบ Trait ทำมาได้ดีมาก ถ้าผ่อนคลาย orphan rule ลงสักหน่อยจะใช้งานได้กว้างกว่านี้มาก
ความเห็นของ GN⁺
-
แม้ความเห็นต่อ Rust จะค่อนข้างเป็นลบโดยรวม แต่เมื่อเป็นข้อสรุปที่ผู้เขียนได้จากการลองมาหลายทาง ก็น่าจะต้องรับฟังอย่างจริงจัง โดยเฉพาะประเด็นที่ว่าความเป็นจริงเชิงปฏิบัติและความเร็วในการพัฒนาสำคัญเพียงใดในโดเมนการพัฒนาเกม และ Rust ยังขาดในด้านนั้นอยู่มากพอสมควร
-
เห็นด้วยว่าความปลอดภัยที่ Rust มุ่งเน้น บางครั้งอาจลดประสิทธิภาพการพัฒนาอย่างรุนแรง เวลาลงมือเขียน Rust มักมีหลายกรณีที่ต้องใช้เวลาไปกับการทำให้คอมไพเลอร์พอใจมากกว่าการพัฒนาตรรกะจริง ๆ ซึ่งดูจะมาจากธรรมชาติที่ Rust เป็นภาษาซึ่งเชี่ยวชาญด้าน system programming โดยพื้นฐาน
-
ในทางกลับกัน การพัฒนาเกมส่วนใหญ่เกิดในสภาพแวดล้อมแบบ single-threaded และให้ความสำคัญกับการทำต้นแบบและการวนซ้ำอย่างรวดเร็ว ดังนั้นการตรวจความปลอดภัยที่เข้มเกินไปอาจกลับกลายเป็นโทษ โดยเฉพาะเคยได้ยินบ่อยถึงประสบการณ์ที่หลายคนพยายามทำเกมด้วย Rust ในช่วงแรกแล้วไปไม่รอด ดูเหมือนว่าปัญหาเชิงรากฐานเหล่านั้นก็ยังไม่ได้รับการแก้ไขมากนัก
-
แน่นอนว่าเอนจินเกมยุคใหม่อย่าง Bevy ช่วยให้การพัฒนาเกมด้วย Rust สะดวกขึ้นบ้าง แต่ก็ยังห่างไกลจากระดับของเอนจินที่โตเต็มที่อย่าง Unity หรือ Godot มาก ดังที่บทความนี้ชี้ให้เห็น การใช้ Rust ควบคู่กับเอนจินที่มีอยู่แล้วอาจเป็นทางเลือกที่สมจริงกว่าการสร้างทุกอย่างขึ้นมาแบบ Rust ล้วน
-
ข้อดีที่ผู้เขียนพูดถึงจะเด่นชัดมากเมื่อใช้ Rust ในงานที่ไม่ใช่เกม โดยเฉพาะด้าน system programming หรือการพัฒนาเว็บเซิร์ฟเวอร์ ซึ่งความปลอดภัยและประสิทธิภาพสูงของ Rust เป็นจุดแข็งสำคัญ แต่ข้อดีเหล่านั้นกลับไม่ได้สร้างคุณค่ามากนักในงานพัฒนาเกม
-
สุดท้ายแล้ว Rust ไม่ใช่ภาษาครอบจักรวาล แต่เป็นภาษาที่เหมาะกับบางโดเมนโดยเฉพาะ ดังนั้นจึงควรพิจารณาอย่างรอบคอบว่าเข้ากับลักษณะของโปรเจกต์ตัวเองหรือไม่
13 ความคิดเห็น
ผมคิดว่า rust เป็นภาษาที่ถูกสร้างขึ้นมาเพื่อแก้ปัญหาที่เกิดจากข้อผิดพลาดหลากหลายรูปแบบของนักพัฒนา ในยุคที่มีนักพัฒนาซึ่งสามารถเขียนโค้ดได้อย่างถูกต้องสมบูรณ์ในระดับแกนหลักลดน้อยลง
มันไม่ได้เหมาะกับการพัฒนาให้เสร็จอย่างรวดเร็ว แต่เหมาะกับการพัฒนาให้ถูกต้องแม่นยำมากกว่า
ดังนั้นจึงไม่ค่อยเหมาะกับโปรเจกต์ที่มีคำขอเพิ่มเติมจากผู้ใช้เกิดขึ้นบ่อยและต้องนำไปทำจริง
ถึงอย่างนั้นก็ตาม ที่ยังคาดหวังให้มี UI library ออกมา
ก็น่าจะเป็นเพราะความรู้สึกคาดหวังว่า ถ้ามีเพียง UI เล็ก ๆ ที่เรียบง่ายซึ่งทำงานอยู่บนโค้ดที่แข็งแกร่ง ก็จะทำให้สามารถนำไปใช้ประโยชน์ได้ดีขึ้นมาก
ผมไม่เคยทำเกมมาก่อนเลยไม่ค่อยรู้เท่าไร แต่ดูเหมือนว่า
อาจเลือกภาษาไม่เหมาะตั้งแต่แรก หรือถ้าการพัฒนาแบบวนรอบรวดเร็วสำคัญก็ควรเลือกภาษาระดับสคริปต์..
หรือไม่ก็อาจยังไม่มีความเข้าใจเชิงลึกเกี่ยวกับระบบมากพอ
ต่อให้เลือก C++ ก็คงเจอปัญหาคล้าย ๆ กันอยู่ดีครับ
ใช่แล้ว ถ้าเลือก C++ ก็ยังมีตัวเลือกอย่าง Unreal อยู่เหมือนกัน...
ทุกวันนี้ผมเริ่มรู้สึกว่า ภาษาระดับเนทีฟอย่าง Rust หรือ C++ ดูจะเหมาะกับการพัฒนามิดเดิลแวร์อย่างเกมเอนจิน มากกว่าการพัฒนาเกมไคลเอนต์เสียอีก
ก็เหมือนกับที่แทบไม่มีกรณีของการทำฟูลสแตกเว็บด้วยภาษาระดับเนทีฟนั่นแหละครับ
มันเป็นภาษาที่ถูกสร้างมาสำหรับจุดประสงค์แบบนั้นตั้งแต่แรกอยู่แล้ว เพื่อมาแทนที่ C++
ดูเหมือนว่าบทความจะกลายเป็นบทความวิจารณ์ตัวภาษาไปเสียแล้ว
ใช่ครับ เดิมทีเริ่มต้นขึ้นมาเพื่อปรับปรุงเอนจินภายในของ Firefox ใช่ไหมครับ?
เนื้อหาให้ความรู้สึกประมาณว่าไปลองพัฒนาด้วย Rust ในสายงานที่ไม่ค่อยเข้ากับมันนักแบบยาครอบจักรวาล แล้วก็เลยลำบากอยู่พอสมควร ฮ่าๆ
เห็นด้วย 100% ครับ สำหรับเกมลอจิก ภาษาโปรแกรมที่มีประสิทธิภาพในการพัฒนาสูงดูจะเหมาะกับกรณีการใช้งานมากกว่า
ฉันเพิ่งอยู่ในช่วงเริ่มต้นใช้ Rust เลยรู้สึกอินพอสมควรครับ
พอมีการเปลี่ยนแปลงเรื่อง borrowing ก็ต้องแก้โค้ดเยอะมากจนทรมานเลยครับ
ต่างจาก python หรือ javascript ที่มีการแสดงออกแบบแฝงอยู่เยอะ Rust มีแนวโน้มจะเขียนแบบชัดเจน จนบางทีกลายเป็นโค้ดที่ยืดยาว และยังทำให้โปรแกรมเมอร์ต้องตัดสินใจหลายอย่างจนรู้สึกเหนื่อยด้วยครับ
ถึงอย่างนั้น แนวคิดที่ภาษาอื่นไม่มีใน Rust ก็ยังสดใหม่และสนุกมากครับ
ถ้าไม่มี Rust Analyzer หรือ GitHub Copilot ผมคงถอดใจไปตั้งแต่แรกแล้วครับ
สมกับที่ว่าการพัฒนาเกมเป็นงานฮาร์ดคอร์จริงๆ...
แทนที่จะเขียนทุกอย่างตั้งแต่แกนหลักไปจนถึงฟรอนต์ด้วย Rust
ถ้าเขียนแกนหลักด้วย Rust แล้วผสานใช้ภาษาที่มีประสิทธิภาพในการพัฒนาสูงกว่า เหมือน Unity หรือเกมเอนจินอื่น ๆ จะเป็นอย่างไร?
ก็คงช่วยไม่ได้ เพราะมันมีด้านที่บังคับกรอบค่อนข้างมากอยู่แล้ว
ความคิดเห็นจาก Hacker News
แชร์ประสบการณ์พัฒนาไคลเอนต์เมตาเวิร์สพร้อมชี้ให้เห็นปัญหาของ Rust
ในฐานะนักพัฒนาเกมที่มีประสบการณ์ 20 ปี มองว่า Rust ไม่เหมาะกับการพัฒนาเกม
ระบบนิเวศการพัฒนาเกมด้วย Rust กำลังพึ่งพากระแส hype มากเกินไป
เปรียบเทียบประสบการณ์พัฒนาเกมด้วย Bevy, Unity และ Godot
ภาษา Nim อาจเหมาะกับการพัฒนาเกมมากกว่า Rust
Rust ดูเป็นภาษาที่เคร่งและบังคับวิธีเขียนโปรแกรมแบบเฉพาะทาง
เดโม tooling ของ Allen Blomquist แสดงให้เห็นว่า feedback loop ในการพัฒนาสำคัญมาก
ปัญหาพื้นฐานของ Rust คือการละทิ้ง object-oriented และโน้มเอียงไปทาง functional programming
สำหรับการพัฒนาเกม Rust คือหนึ่งในตัวเลือกที่แย่ที่สุด
ระหว่างเขียนโค้ดด้วยภาษา D พบว่าการเปลี่ยน data layout ทำได้ง่ายกว่า C มาก