- วิศวกรซอฟต์แวร์ที่มีประสบการณ์ 15 ปี แชร์ประสบการณ์ พัฒนาเกมไพ่จากวัยเด็กด้วยภาษา Go
- ตอนพัฒนา “Truco” โดยไม่มี LLM (Large Language Model) ต้องแก้ทุกปัญหาด้วยตนเองทั้งหมด ไม่ว่าจะเป็น การออกแบบ UI และการดีพลอยแบบ serverless จนใช้เวลา 3 เดือน
- ตอนทำ “Escoba” ใช้ LLM ช่วย แปลงโค้ดแบ็กเอนด์และเร่งความเร็วในการพัฒนา อย่างมาก โดย พรอมป์ครั้งเดียวก็ใช้งานได้เกือบทั้งหมด
- ช่วงท้ายของบทความมี ตัวอย่าง Tic-Tac-Toe พร้อมไกด์แบบ ทีละขั้นตอน สำหรับสร้างเกมด้วย Go backend, การแปลงเป็น WASM และการเชื่อมกับ React เพื่อให้ใครก็เริ่มทำเกมเองได้
- แต่ ฟรอนต์เอนด์ React และการจัดการสถานะเกมบน WASM ก็ยังต้องดีบักและลงมือทำเองอยู่ดี
บทนำ
- วิศวกรซอฟต์แวร์ที่มีประสบการณ์ 15 ปีตระหนักว่าตัวเองยังไม่เคยสร้างและเผยแพร่เกมจริง ๆ มาก่อน
- จึงตัดสินใจพัฒนาเกมไพ่เกมหนึ่งที่เคยเล่นกับเพื่อน ๆ ในอาร์เจนตินาตอนเด็กด้วยภาษา Go
Truco: 3 เดือนโดยไม่มี LLM
- เริ่มพัฒนาเกมไพ่ชื่อ Truco ตั้งแต่วันที่ 18 มิถุนายน 2024 โดยใช้ Go backend และเขียนฟรอนต์เอนด์ด้วยความรู้ React เพียงเล็กน้อย
- ความท้าทายใหญ่ที่สุดคือ การทำ UI และเพื่อหลีกเลี่ยงการต้องมีเซิร์ฟเวอร์ จึงใช้ TinyGo เพื่อ transpile เป็น WASM (WebAssembly) แล้วนำไฟล์ static ไปดีพลอยบน GitHub Pages
- ในช่วงที่ยังไม่มี LLM ผู้เขียนต้องค้นหารายละเอียดทุกอย่างเองและลองผิดลองถูกอยู่ตลอด จนใช้เวลาราว 3 เดือนกว่าจะเสร็จ
- ไม่มีเป้าหมายด้านโฆษณาหรือรายได้ แค่ต้องการทำเกมนี้ให้เสร็จ และแม้จะเปิดตัวมาแล้ว 1 ปี ก็ยังมีคนเล่นอย่างต่อเนื่อง
Escoba: 3 วันเมื่อมี LLM
- หนึ่งปีต่อมา ระหว่างเดินทางไปอาร์เจนตินาเพื่อเยี่ยมครอบครัว เขาได้สอนหลานเล่นเกมไพ่ยอดนิยมอันดับสองชื่อ Escoba
- ครั้งนี้เขาใช้ LLM (Claude) โดยคัดลอกแบ็กเอนด์ของ Truco มาก่อน จากนั้นอธิบายกติกาของ Escoba ผ่านพรอมป์และขอให้รีแฟกเตอร์โค้ด
- แค่พรอมป์แรกก็ได้ผลลัพธ์ที่เกือบสมบูรณ์แล้ว เหลือเพียงบั๊กเล็กน้อยและฟีเจอร์เสริมบางส่วนที่ต้องแก้ด้วยมือ
- ส่วนฟรอนต์เอนด์ยังต้องลงมือทำและดีบักเองอีกหลายวัน ข้อจำกัดของ LLM, ทักษะ React และสภาพแวดล้อมที่ค่อนข้างแปลกซึ่งให้ WASM เป็นตัวจัดการสถานะเกม ล้วนเป็นความท้าทาย
ทีละขั้นตอน: วิธีสร้างเกมของตัวเอง
- ผู้เขียนแนะนำ ไกด์ภาคปฏิบัติขั้นต่ำและโค้ดตัวอย่าง เพื่อให้คนอื่นลองพัฒนาเกมด้วยตัวเองได้
- มีรีโพซิทอรีตัวอย่าง Tic-Tac-Toe ให้ fork ไปเริ่มต้นได้ทันที
การพัฒนาแบ็กเอนด์
- แบ็กเอนด์แบบ turn-based สามารถออกแบบฟังก์ชันการทำงานได้อย่างชัดเจน
- หากต้องการคงโครงสร้างแบบ serverless ไว้ การหลีกเลี่ยงรูปแบบที่ผู้เล่นหลายคนต้องเล่นกันแบบคนต่อคนถือเป็นทางเลือกที่สมจริง หากไม่มีเซิร์ฟเวอร์เชิงพาณิชย์รองรับ
การพัฒนาฟรอนต์เอนด์
- ฟรอนต์เอนด์ต้องรองรับงานต่อไปนี้
- ขอสร้าง
GameState ใหม่จากแบ็กเอนด์
- แสดงสถานะบน UI
- มีอินเทอร์เฟซให้เลือกการกระทำที่ถูกต้อง
- เมื่อมีการกระทำ ให้ส่งคำสั่งไปยังแบ็กเอนด์
- หากถึงตาของบอท ให้ร้องขอไปยังแบ็กเอนด์
การแปลงแบ็กเอนด์เป็น WASM
- หากต้องการ build โค้ด Go ให้เป็น WASM ให้ใช้
GOARCH=wasm GOOS=js go build
- อาจมีปัญหาเรื่องขนาดไบนารีที่ใหญ่ จึงใช้ TinyGo เพื่อลดขนาด
- เพื่อ export ฟังก์ชันที่จะเชื่อมกับฟรอนต์เอนด์ ต้องเขียน entry point แยกใน Go (เช่น
main_wasm.go) แล้วแยกการ build ตามกรณี
- ในฟังก์ชันหลักต้องบล็อกด้วย
select {} เพื่อไม่ให้โปรแกรมจบทันที
การเชื่อมข้อมูลระหว่างแบ็กเอนด์กับฟรอนต์เอนด์
- struct แบบยืดหยุ่นของ Go อย่าง
GameState ไม่สามารถ serialize/deserialize จาก WASM ได้โดยตรง
- จึงจำเป็นต้อง แลกเปลี่ยนข้อมูลทั้งหมดในรูปแบบ JSON
- โดยอ้างอิงเอกสารของ TinyGo ทั้งอินพุตและเอาต์พุตจะต้องส่งผ่านการ serialize เป็น JSON
อินเทอร์เฟซระหว่างฟรอนต์เอนด์กับแบ็กเอนด์
- ฝั่งฟรอนต์เอนด์จะเรียกใช้ฟังก์ชันของ backend โดยตรง
GameState ถูกจัดการอยู่ภายใน WASM เท่านั้น ฟรอนต์เอนด์ไม่สามารถ mutation ได้ และ backend คือแหล่งข้อมูลจริงเพียงหนึ่งเดียวเสมอ
- หลัง recompile WASM ต้องนำไฟล์ใหม่มาแทนที่ และมีตัวอย่างการใช้ Makefile เพื่อทำงานอัตโนมัติ
สภาพแวดล้อมการรัน WASM
- ในการรัน ต้องใส่
wasm_exec.js ไว้ใน head และใช้สคริปต์ดังกล่าวเพื่อสร้าง instance แล้วจึงรัน
บทสรุป
- การสร้างเกมเป็นประสบการณ์ที่สนุก และแนวทางที่ใช้ Go, WASM และ React ร่วมกันก็เป็นสิ่งที่ใคร ๆ ลองทำตามได้
- แม้ ความช่วยเหลือจาก LLM จะเพิ่มผลิตภาพได้มาก แต่ ทักษะด้านฟรอนต์เอนด์และประสบการณ์ในการดีบัก ก็ยังสำคัญอยู่เสมอ
- ใคร ๆ ก็สามารถ ลองพัฒนาเกมด้วยตัวเอง ด้วยโครงสร้างแบบนี้ได้ จึงน่าลองทำดู
ยังไม่มีความคิดเห็น