บันทึกการพัฒนา "Machine" ของ xkcd
(chromakode.com)- Machine ของ xkcd คือเกมเครื่องลูกแก้วแบบ Rube Goldberg ขนาดยักษ์ที่ประกอบจากไทล์อุปกรณ์ที่ผู้อ่านสร้างขึ้น โดยนำไอเดียมาทำเป็นการ์ตูนอินเทอร์แอ็กทีฟจริงภายใน 3 สัปดาห์
- ประสบการณ์จากโปรเจกต์แบบมีส่วนร่วมของผู้ใช้ก่อนหน้านี้นำไปสู่หลักเกณฑ์การออกแบบว่า หากแคนวาสร่วมกันจะทำงานได้ดี ต้องมี บริบทร่วมและเป้าหมายร่วม
- เพื่อรักษาความสามารถในการแสดงออกของผู้เล่น แต่ยังคงความเข้ากันได้ของไทล์ จึงกำหนดข้อจำกัดด้านอินพุตและเอาต์พุตอย่างเข้มงวด และจำกัดให้อุปกรณ์แต่ละชิ้นต้องเข้าสู่ สภาวะเสถียรภายใน 30 วินาที
- แทนที่จะจำลองทั้งเครื่องทั้งหมดแบบเรียลไทม์ จะใช้ Rapier เฉพาะบริเวณที่มองเห็น และทำให้ดูเหมือนอุปกรณ์กำลังทำงานอยู่แล้วด้วย สแนปช็อต ณ เวลาที่อนุมัติ
- ใช้ React และการเรนเดอร์ด้วย DOM, แบ็กเอนด์ Haskell, Redis, OpenAPI, TanStack Query และ UI สำหรับม็อดเดอเรชัน มาร่วมกันจัดการขั้นตอนการอนุมัติและเผยแพร่ผลงานส่งเข้าระบบ
จุดเริ่มต้นของ Machine
- xkcd เปิดตัว Machine เมื่อวันที่ 5 เมษายน
- Machine คือเครื่องมือสร้าง Rube Goldberg machine ขนาดใหญ่ในสไตล์เกมคลาสสิก The Incredible Machine
- เครื่องทั้งหมดประกอบขึ้นจากการต่อกันของไทล์อุปกรณ์ขนาดเล็กที่ผู้อ่าน xkcd แต่ละคนสร้างไว้
- ทีมสร้าง Machine ขึ้นภายใน 3 สัปดาห์ และไอเดียเริ่มต้นมาจาก GIF แบบร่วมมือกันในปี 2005 ชื่อ Blue Ball Machine
- คำถามหลักในการระดมไอเดียช่วงแรกคือ ลูกแก้วมาจากไหน เครื่องที่ทุกคนเห็นเป็นเครื่องเดียวกันอย่างไร เป้าหมายของเครื่องคืออะไร ผู้เล่นจะโต้ตอบกันอย่างไร และอะไรคือแรงจูงใจในการเข้าร่วม
บทเรียนจาก xkcd แบบมีส่วนร่วมของผู้ใช้
- ในการ์ตูนอินเทอร์แอ็กทีฟ xkcd ก่อนหน้านี้ที่เน้นคอนเทนต์จากผู้ใช้ Lorenz ให้ผู้อ่านเขียนข้อความในแต่ละพาเนลเพื่อพัฒนามุกและเรื่องราว ซึ่งเป็นประสบการณ์ที่ดี
- ส่วน Collector’s Edition ในปี 2020 ให้ผู้เล่นค้นหาสติกเกอร์จากคลัง xkcd แล้วนำไปติดบนแคนวาสร่วมกันทั่วโลกได้คนละครั้ง แต่ผลลัพธ์ไม่เป็นไปอย่างที่หวัง
- ผู้เล่นทุกคนเริ่มจากจุดกึ่งกลางของแผนที่ว่างเปล่า และไม่นานหน้าจอก็กลายเป็นภาพที่ชวนสับสนตั้งแต่แรกเห็น
- แรงจูงใจในการเลือกตำแหน่งติดสติกเกอร์อย่างรอบคอบมีน้อย และยากที่จะทำให้เนื้อเรื่องคืบหน้าด้วยการกระทำของแต่ละคนเพียงลำพัง
- เพราะไม่มีเรื่องเล่าหรือเป้าหมายร่วมกัน จึงไม่ชัดเจนว่าสติกเกอร์แต่ละชิ้นเชื่อมโยงกับองค์ประกอบอื่นของหน้าอย่างไร
- ถ้าจะแคนวาสร่วมกันให้ทำงานได้ดี ผู้ใช้ต้องสามารถ เรียนรู้จากตัวอย่าง ว่าควรสร้างอะไรถึงจะออกมาดูน่าสนใจ
- หากต้องการให้ผลลัพธ์เชิงสร้างสรรค์ไปในทิศทางเดียวกัน จำเป็นต้องมี บริบทร่วมและเป้าหมายร่วม ที่ช่วยซิงก์ว่าควรสร้างอะไร
การออกแบบข้อจำกัด: การแสดงออก ความเข้ากันได้ และสภาวะเสถียรใน 30 วินาที
- แม้จะตัดสินใจทำอุปกรณ์ลูกแก้วแบบร่วมมือกันขนาดใหญ่แล้ว ก็ยังเหลือปัญหาเรื่องขนาดเครื่องทั้งหมด วิธีจำลอง และวิธีรวมไทล์เข้าด้วยกัน
- หากสมมติว่าเครื่องมีขนาด 100x100 เป้าหมายที่จะให้ไคลเอนต์จำลอง 10,000 ไทล์ แบบเรียลไทม์และรองรับลูกแก้วหลายสิบลูกในแต่ละไทล์ถือว่าเสี่ยงเกินไป
-
ให้ความสำคัญกับการแสดงออกมากกว่าความแม่นยำ
- มีการพิจารณาทั้งการรันเครื่องทั้งหมดบนเซิร์ฟเวอร์ หรือจำลองไทล์แต่ละชิ้นเพื่อตรวจสอบความถูกต้อง
- แต่เมื่อใช้ตัวแก้ไขต้นแบบ พบว่าสามารถสร้างรูปแบบการชนของลูกแก้วที่วุ่นวายและคาดเดาไม่ได้อย่างง่ายดาย จึงสรุปว่าหากบังคับให้เครื่องต้องคาดการณ์ได้ อิสระของผู้เล่นจะลดลง
- แบบออกแบบสุดท้ายจึงให้ความสำคัญกับ ความยืดหยุ่นของผู้เล่น เป็นหลัก แม้จะหมายถึงการสร้างอุปกรณ์ที่ไม่กำหนดแน่ชัดมาก ๆ หรือแม้แต่อุปกรณ์ที่พังได้
- การเลือกแบบนี้ทำให้ต้องมี การม็อดเดอเรตเชิงรุก เพื่อตรวจว่าไทล์เป็นไปตามข้อจำกัดหรือไม่ และเพื่อลบคอนเทนต์ไม่เหมาะสม
-
ข้อจำกัดอินพุต·เอาต์พุตเพื่อให้ไทล์เข้ากันได้
- ในช่วงแรกมีการคิดถึงวิธีให้ผู้เล่นคนถัดไปต่อขยายได้อย่างอิสระตามตำแหน่งเอาต์พุตของไทล์ก่อนหน้า
- แต่ถ้าจำเป็นต้องเปลี่ยนไทล์ที่วางไว้ก่อนหน้านั้นในภายหลัง พื้นที่ขนาดใหญ่ที่พึ่งพาไทล์นั้นอาจพังตามไปด้วย
- จึงกำหนดข้อจำกัดอินพุตและเอาต์พุตอย่างเข้มงวด เพื่อให้ผู้เล่นหลายคนสามารถออกแบบสิ่งที่เข้ากันได้ภายในพื้นที่ไทล์เดียวกัน
- แนวทางนี้สอดคล้องกับหลักการ Robustness principle ที่ว่า “สิ่งที่ส่งออกควรเข้มงวด สิ่งที่รับเข้าควรยืดหยุ่น”
- map generator ของ Kevin เริ่มจากปริศนาแบบ 1 อินพุต 1 เอาต์พุตที่เรียบง่าย จากนั้นซับซ้อนขึ้นเป็นการรวมกันตรงกลางแบบ 4 อินพุต 4 เอาต์พุต แล้วค่อยกลับมาที่ 2 เอาต์พุตต่อไทล์ในช่วงท้าย
- ตัวแก้ไขให้ฟีดแบ็กแบบเรียลไทม์ขณะที่ผู้เล่นสร้างไทล์
- โดยเฉลี่ยแล้ว ไทล์ควรปล่อยลูกแก้วออกไปในอัตราใกล้เคียงกับที่รับเข้ามา
- พยายามลดอุปกรณ์ที่กลืนลูกแก้วหายไปหรือทำให้เกิดการหน่วงอย่างมาก
- เพื่อสะท้อนความผันผวนของอินพุตจากต้นน้ำ จึงใช้ chaos testing โดยสุ่มอัตราความเร็วของลูกแก้วที่ไหลเข้ามาในตัวแก้ไข
-
ต้องเข้าสู่สภาวะเสถียรภายใน 30 วินาที
- เพื่อลดเวลาที่ม็อดเดอเรเตอร์ต้องเฝ้าดู จึงตั้งเกณฑ์ตามอำเภอใจว่าอุปกรณ์ต้องเข้าสู่ สภาวะเสถียรภายใน 30 วินาที
- เกณฑ์นี้อ้างอิงจากการคำนวณว่า หากต้องดู 10,000 ไทล์ ไทล์ละ 30 วินาที เวลาม็อดเดอเรตรวมจะอยู่ที่ประมาณ 83.3 ชั่วโมง
- ลูกแก้วก็ถูกตั้งให้หมดอายุหลัง 30 วินาทีด้วย
- หากไม่มีการหมดอายุ ประสบการณ์แรกของผู้เล่นมือใหม่จะกลายเป็นการเห็นลูกแก้วกองสะสมเต็มหน้าจอ
- จำนวน active rigid body ก็จะเพิ่มขึ้นจนทำให้การจำลองฟิสิกส์ช้าลงด้วย
- การหมดอายุของลูกแก้วช่วยไม่ให้ข้อผิดพลาดสะสมตามเวลา และทำให้การเฝ้าดูเพียง 30 วินาทีก็เพียงพอที่จะเห็นตำแหน่งปลายทางของลูกแก้วส่วนใหญ่ จึงทำให้การม็อดเดอเรตง่ายขึ้น
วิธีที่ไม่รันทั้งเครื่องแบบเรียลไทม์
- สมมติฐานใหญ่ข้อแรกของสถาปัตยกรรม Machine คือ หากรักษาข้อจำกัดข้างต้นไว้ ก็สามารถต่อไทล์ต่าง ๆ เข้าด้วยกันให้ดูเหมือนเป็นเครื่องทั้งหมดได้
- พวกเขาตรวจสอบสมมติฐานนี้ด้วยการสร้างและลองแก้แผนที่ขนาดเล็กหลายชุด
- เนื่องจากไม่สามารถรันเครื่องทั้งหมดแบบเรียลไทม์ได้ทั้งบนเซิร์ฟเวอร์หรือไคลเอนต์ จึงจำเป็นต้องใช้วิธีที่จำลองเฉพาะบริเวณรอบ ๆ ที่ผู้ใช้กำลังมองเห็น
- เป้าหมายคือทำให้ผู้ใช้สามารถตามลูกแก้วลูกหนึ่งจากด้านบนของเครื่องลงมาจนถึงด้านล่างได้
-
โลกฟิสิกส์ที่มีอยู่เฉพาะในบริเวณที่มองเห็น
- ตัวดูแผนที่ในช่วงแรกจำลองเฉพาะบริเวณที่มองเห็น แต่เมื่อเลื่อนหน้าจอ ไทล์ที่เพิ่งเข้ามาใหม่จะเริ่มจากสถานะว่าง ทำให้เห็นช่องโหว่ของการไหล
- เพื่อไม่ให้เป็นไทล์ว่าง จึงเลือกเก็บ สแนปช็อต ของไทล์ที่เข้าสู่สภาวะเสถียรแล้ว และโหลดมันก่อนเข้าสู่หน้าจอเล็กน้อย เพื่อให้ดูเหมือนกำลังทำงานอยู่แล้ว
- ในการ์ตูนฉบับสุดท้าย สิ่งที่มีอยู่จริงในระบบจำลองฟิสิกส์มีเพียงไทล์ที่กำลังถูกเรนเดอร์เท่านั้น
- เพื่อให้ดูเหมือนว่ามีเครื่องต่อขึ้นไปอีกด้านบนของหน้าจอ ไทล์ในแถวบนสุดของการจำลองจะถูกป้อนลูกแก้วที่สร้างขึ้นตามอัตราที่คาดไว้จากข้อจำกัดอินพุต
-
สแนปช็อต ณ เวลาที่อนุมัติ
- การสร้างสแนปช็อตเชื่อมอยู่กับ UI ของม็อดเดอเรชัน
- ม็อดเดอเรเตอร์ต้องรออย่างน้อย 30 วินาทีก่อนอนุมัติไทล์ และสถานะ ณ ตอนที่กดปุ่มอนุมัติจะถูกบันทึกเป็นสแนปช็อต
- ม็อดเดอเรเตอร์ยังมีดุลยพินิจที่จะรอเพิ่มอีกเล็กน้อยจนกว่าอุปกรณ์จะอยู่ในสภาพที่ดูดี
- วิธีสแนปช็อตยังมีผลเหมือนการรีเซ็ตข้อผิดพลาดสะสม ดังนั้นเมื่อผู้ใช้เลื่อนมาเห็นไทล์ใหม่เป็นครั้งแรก ก็จะได้เห็นสถานะสะอาดที่ม็อดเดอเรเตอร์เห็นว่าดีแล้ว
- หากเฝ้าดูนานมาก อุปกรณ์หลายชิ้นอาจหยุดนิ่งหรือพังได้ แต่ถ้ายังคงสำรวจต่อไป ก็จะเจอสแนปช็อตใหม่เรื่อย ๆ
- เครื่องทั้งหมดไม่ได้ถูกจำลองอย่างสมบูรณ์ และผลลัพธ์ที่ได้ก็ใกล้เคียงกับ hyperreality
โครงสร้างการเรนเดอร์ด้วย React, DOM และ Rapier
- Machine สร้างขึ้นบนเอนจินฟิสิกส์ Rapier
- จุดเด่นของ Rapier คือเอกสารประกอบ, API, องค์ประกอบพื้นฐานที่มีประโยชน์ และประสิทธิภาพบนเบราว์เซอร์ผ่าน WASM จากการเขียนด้วย Rust
- ตอนแรกก็สนใจเรื่องการรับประกันความกำหนดแน่ชัดของ Rapier เช่นกัน แต่ท้ายที่สุดไม่ได้ทำการจำลองฝั่งเซิร์ฟเวอร์
- บน Rapier มีการเขียน React context แบบกำหนดเองชื่อ
<PhysicsContext>- ใช้สร้างและจัดการอ็อบเจ็กต์ฟิสิกส์ของ Rapier ภายในวงจรชีวิตของคอมโพเนนต์ React
- ทำให้พัฒนาแต่ละวัตถุที่วางได้และพื้นผิวชนต่าง ๆ ได้ง่ายในรูปแบบคอมโพเนนต์ “widget”
- React ทำหน้าที่เหมือน scene graph แบบหยาบ ๆ ที่รวดเร็ว
- เมื่อไทล์ถูก unmount อ็อบเจ็กต์ฟิสิกส์และ DOM ที่เกี่ยวข้องก็จะถูกเก็บกวาดไปด้วย
- การ hot reload ผ่านการรีเฟรชอย่างรวดเร็วช่วยให้ปรับรูปทรงการชนได้ง่าย
- มีการทำให้ฟิสิกส์ฮุกใช้งานไม่ได้หากอยู่นอก
<PhysicsContext>ซึ่งนำไปใช้กับภาพพรีวิวแบบสแตติกใน UI ม็อดเดอเรชัน - ภายหลังมองว่าการสร้างอ็อบเจ็กต์ Rapier ในรูปคอมโพเนนต์แทนฮุกน่าจะดีกว่า
- react-three-rapier ใช้วิธีนี้ และเข้ากับการ diff ของ React ได้ดีกว่า
- แนวทางที่อิง
useEffectจะทำให้เมื่อ dependency เปลี่ยน อินสแตนซ์เดิมถูกลบทิ้งแล้วสร้างใหม่
-
การเรนเดอร์ด้วย DOM ล้วน
- Machine เรนเดอร์ทั้งหมดด้วย DOM
- ช่วงแรกคิดว่าหากเจอข้อจำกัดด้านประสิทธิภาพ อาจย้ายไปใช้ PixiJS หรือ canvas ได้ แต่สุดท้ายก็ผลักแนวทาง DOM ให้สุดเพราะมีสิ่งที่ต้องสร้างน้อยกว่า
- เพื่อประสิทธิภาพการเรนเดอร์ ลูปเฟรมจะนำสไตล์ไปใช้กับวิดเจ็ตที่อยู่ในการจำลองฟิสิกส์โดยตรง
- React diff จะทำงานเฉพาะตอนที่โครงสร้างของ scene graph เปลี่ยนเท่านั้น
- ตอนแรกเรนเดอร์ลูกแก้วด้วย React เช่นกัน แต่การสร้างและลบบ่อย ๆ ทำให้ต้นทุน diff สูง จึงสร้าง optimized renderer แยกต่างหาก
- ใช้ draw culling กับลูกแก้วและวิดเจ็ตที่อยู่นอกหน้าจอ
- วิธีนี้ทำงานได้ดีทั้งกับลูกแก้วที่กำลังถูกจำลอง 4,000 ลูก และลูกแก้วหลายร้อยลูกบนหน้าจอ จึงสรุปให้ใช้การเรนเดอร์ด้วย DOM ล้วน
API, ม็อดเดอเรชัน และการจัดการผลงานส่งเข้า
- แบ็กเอนด์เขียนด้วย Haskell โดย davean และ Kevin และใช้ Redis เป็นสตอเรจ
- การแชร์ type ระหว่างโค้ดเบสใช้ OpenAPI และ OpenAPI fetch
- ช่วงแรกมีความไม่สะดวกอยู่บ้างในการทำให้ตรงกับ Haskell type
- แต่ก็ช่วยมากในการประสานการเปลี่ยนแปลง API ช่วงท้ายโครงการ
- TanStack Query มีประโยชน์ในการจัดการแคชและการรีเฟรชอัตโนมัติโดยไม่ต้องมี server push
-
UI ม็อดเดอเรชันและลำดับความสำคัญ
- UI ม็อดเดอเรชันที่ Ed White ออกแบบคือคอขวดที่ผลงานส่งเข้าทั้งหมดต้องผ่านก่อนจะเผยแพร่สู่สาธารณะ
- ม็อดเดอเรเตอร์อาจต้องเลือกจากดีไซน์ผู้สมัครหลายร้อยแบบสำหรับไทล์หนึ่ง ๆ
- ลำดับความสำคัญของคิวกำหนดด้วย interestingness score แยกตามชนิดวิดเจ็ต แล้วนับจำนวนแต่ละอินสแตนซ์เพื่อนำมาจัดเรียงไทล์ผู้สมัคร
- วิธีนี้มีแนวโน้มเอนเอียงไปทางคำตอบที่ใส่องค์ประกอบจำนวนมาก แต่ม็อดเดอเรเตอร์ก็ช่วยถ่วงดุลด้วยการตรวจดูช่วงกลางของรายการเพื่อหาแบบที่มินิมัลกว่านั้น
- ความไม่สมดุลอย่างมากระหว่างจำนวนดีไซน์ที่ถูกส่งเข้ามา กับจำนวนดีไซน์ที่ถูกเผยแพร่ในเครื่องจริง ยังคงเป็นจุดที่น่าเสียดาย
- ก่อนเปิดตัวมีการพยายามหาวิธีเปิดเผย backlog ให้มากขึ้น แต่ไม่พบจุดลงตัวที่ดีภายใต้ข้อจำกัดด้านเวลาม็อดเดอเรชัน
- หลังจากการส่งผลงานสดสิ้นสุดลง ก็อยากหาวิธีแบ่งปันชุดข้อมูลผลงานส่งเข้ามากขึ้น
-
คูลดาวน์การอนุมัติและการเร่งความเร็ว
- เนื่องจากคุณภาพของสแนปช็อตไทล์สำคัญมาก ปุ่มอนุมัติของม็อดเดอเรเตอร์จึงถูกปิดไว้จนกว่าจะรันการจำลองอย่างน้อย 30 วินาที
- คูลดาวน์นี้ช่วยให้ได้สแนปช็อตในสภาวะเสถียร และตรวจสอบว่าเอาต์พุตรับลูกแก้วในอัตราที่คาดไว้หรือไม่
- ตอนแรกคาดว่าม็อดเดอเรเตอร์จะรำคาญกับสิ่งนี้ แต่กลับได้รับการตอบรับในทางบวกเพราะช่วยป้องกันการตัดสินใจที่รีบเกินไป
- หลังเปิดตัว มีการเพิ่มสไลเดอร์ให้ม็อดเดอเรเตอร์สามารถรันการจำลองได้เร็วกว่าเวลาจริงมาก
- ฟีเจอร์นี้ทำให้สามารถดู 30 วินาทีแรกของผลงานส่งเข้าได้ในเวลา น้อยกว่า 5 วินาที และตรวจสอบพฤติกรรมในช่วงเวลาที่ยาวกว่านั้นได้ง่ายขึ้น
ปฏิสัมพันธ์ระหว่างไทล์ที่ไม่ได้ตั้งใจ
- “Jamslunt Interfoggle” เป็นอุปกรณ์ที่ถูกอัปโหลดในช่วงไม่กี่ชั่วโมงแรกหลังเปิดตัว โดยใช้กลไกที่อาศัยช่วงการทำงานแคบ ๆ ของพัดลม
- อุปกรณ์นี้รวบรวมลูกแก้วสีน้ำเงินไว้ในทางเดิน และเมื่อมีน้ำหนักมากพอก็จะเททะลักออกทั้งสองด้าน
- “Bouncy” ที่วางอยู่ด้านบน เป็นเอนจินความโกลาหลที่ยิงลูกแก้วผ่านเส้นทางตัดกันสามทาง
- Bouncy บางครั้งส่งลูกแก้วสีเขียวไปยังเอาต์พุตผิดทาง และลูกแก้วนี้ไปทำลายกองลูกแก้วสีน้ำเงินที่ค้างอยู่ จนเกิดการไหลต่อเนื่องเข้า Interfoggle
- ในตัวแก้ไขมีการป้อนเฉพาะสีที่ถูกต้องเพื่อให้อินพุตเข้าใจง่าย ดังนั้น Interfoggle จึงไม่ได้ถูกออกแบบโดยคำนึงถึงพฤติกรรมของลูกแก้วสีเขียวนี้
- การผสมผสานที่ไม่ได้ตั้งใจแบบนี้ กลายเป็นหนึ่งในความสนุกใหญ่ของโปรเจกต์ เมื่อผู้คนใช้เครื่องมืออย่างสร้างสรรค์บนแคนวาสร่วมกัน
โค้ดและการทดลองที่ยังเหลืออยู่
- ดูซอร์สโค้ดของ Machine ได้ที่ GitHub repository
- การทำ implementation ที่จำลองทั้งเครื่องแบบสมบูรณ์ในระดับ global ยังคงเป็นโจทย์แฮ็กที่น่าสนใจ
- ลิงก์สำหรับเพิ่มดีไซน์เข้าไปใน Machine โดยตรงอยู่ที่ xkcd 2916
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
สิ่งที่ตลกสำหรับผมตอนอ่านบทความนี้คือ ตอนนั้นผมไม่รู้เลยว่ากำลังเกิดเรื่องแบบนี้อยู่
ดูเหมือนไม่มีคำอธิบายว่ามันคือสถานการณ์อะไร ไม่รู้ด้วยว่านี่เป็นประสบการณ์ที่ทุกคนทำร่วมกัน และรู้สึกแค่ว่ามีเรื่องสุ่ม ๆ หลายอย่างเกิดขึ้นอย่างสับสน
ผมทำไทล์เสร็จไปสองสามอันแล้วส่งไป คิดว่านั่นคือวิธีไป “ด่านถัดไป” เลยตั้งชื่อโง่ ๆ อย่าง “test 1b” เพราะคิดว่าเป็น โหมดเล่นคนเดียว และชื่อมีแค่ผมที่เห็น
ทำไปไม่กี่อันก็เบื่อ แล้วเดินดูไปเรื่อย ๆ เห็นของซับซ้อนหลายอย่าง แต่ไม่รู้ว่านั่นเป็นงานที่ส่งเข้ามา คิดว่าเป็นแค่จุดเริ่มต้นสำหรับแก้เลเวล สุดท้ายก็เท่ากับว่าผมโดน มุกวัน April Fools’ หลอกเข้าให้
อาจเป็นเพราะไม่เคยเล่นเกมเครื่องจักรต้นฉบับที่เป็นแรงบันดาลใจมาก่อนก็ได้ :-)
ผมเพิ่มองค์ประกอบ “bonk” เข้าไปเยอะ ๆ แล้วเหมือนจะทำให้ rapier ตายไปเลย
Uncaught Error: recursive use of an object detected which would lead to unsafe aliasing in rustat jt (rapier_wasm2d_bg.js:4836:11)at 4ea5626ea4b1e4145572.module.wasm:0xf061cat 4ea5626ea4b1e4145572.module.wasm:0xf0638at 4ea5626ea4b1e4145572.module.wasm:0xb5e7bat H.remove (rapier_wasm2d_bg.js:1051:14)at l.remove (collider_set.js:87:18)at y.removeCollider (world.js:343:28)at PhysicsContext.tsx:258:15แต่ก็สนุกมากจริง ๆ เสียดายที่ไม่รู้ตอนมันเปิดให้เล่นแบบเรียลไทม์ ถ้าสร้าง ลิงก์ถาวร ไปยังเครื่องแต่ละชิ้นที่คนทำได้ก็คงดีมาก
เข้าใจว่าอาจมีปัญหาเรื่องพื้นที่จัดเก็บ แต่ทำแบบเข้ารหัส JSON เป็น base64 แล้วใส่ในพารามิเตอร์ URL ไม่ได้เหรอ? อยากทำแมปแปลก ๆ แล้วแชร์กับคนอื่น
เครื่องที่เข้าไปอยู่ในเวอร์ชันสาธารณะทั้งหมดสามารถทำลิงก์ถาวรได้ แต่ผลงานรายชิ้นที่ไม่ได้ถูกเลือกจากคิวรอตรวจไม่มีลิงก์ถาวร
ตั้งใจตัดสินใจแบบนั้นเพื่อหลีกเลี่ยงความเสี่ยงจากการโฮสต์ เนื้อหาที่ผู้ใช้สร้างขึ้นซึ่งยังไม่ได้ตรวจสอบ บนโดเมนของการ์ตูน
เผื่อไว้ HN เคยมีหัวข้อนี้เมื่อวันที่ 6 เมษายนด้วย และมีคอมเมนต์ 14 รายการ
https://news.ycombinator.com/item?id=39953514
“ไม่มีแรงจูงใจให้คิดอย่างรอบคอบว่าจะติดสติกเกอร์ตรงไหน ผู้เล่นไม่มีอำนาจริเริ่มมากพอที่จะผลักดันเนื้อเรื่องผ่านการกระทำรายครั้งของตัวเอง ดังนั้นความคิดสร้างสรรค์จึงถูกจำกัดอยู่กับรูปแบบง่าย ๆ เช่น การวางสติกเกอร์ที่คล้ายกันซ้ำ ๆ แบบไทล์ หรือการสร้างเส้น”
อ้อ เกมกลายเป็น ชีวิตการทำงานในบริษัทใหญ่ ไปแล้วสินะ
ผมได้ลองร่วมตอนที่เปิดให้เล่น คิดว่าใช้เวลาไปประมาณหนึ่งชั่วโมงเพื่อทำให้ลูกบอลที่ถูกต้องไปยังเอาต์พุตที่ถูกต้องอย่างน่าเชื่อถือที่สุด
หลังส่งแล้วรีเฟรชหน้า ก็มีอุปกรณ์ของคนอื่นอยู่ตรงนั้นแทน ยอมรับว่าสวยกว่าก็จริง แต่ ความน่าเชื่อถือ ต่ำกว่า
อยากให้บอกตั้งแต่เนิ่น ๆ กว่านี้ว่าวิธีการเป็นแบบนั้น และดูเหมือนผมคงไม่ใช่คนเดียวที่ไม่รู้ว่ารายการบล็อกก่อสร้าง เลื่อนได้
ตอนนี้ไม่มีแรงกลับไปตรวจแล้ว :(
ว้าว ผมกับเพื่อนก็เคยนึกไอเดียเดียวกันได้ในปี 2014 แล้วทำเป็นเกมสำหรับ Ludum Dare ด้วย https://nickfa.ro/wiki/CoinSlot
ดีจังที่ได้เห็นไอเดียนั้นถูกขัดเกลาและออกมาในรูปแบบที่ใช้งานได้ดี
ทำให้นึกถึงสิ่งนี้สมัยยังเด็ก ผมเคยเสียเวลาไปมากมายอย่างสนุกสุด ๆ
https://www.myabandonware.com/game/the-incredible-machine-1m...
เหมือนผมพลาดอะไรไป ทำไมองค์ประกอบบางอย่างในเครื่องถึงดูเหมือนมีผลเฉพาะกับลูกบอลบางสีเท่านั้น?
ผมเดาว่าเป็นกลไกเพื่อกันไม่ให้สีปนกันมั่ว แต่เหมือนบทความนี้ไม่ได้อธิบายไว้
ลูกบอลสีเหลืองเบาและมีแรงต้านอากาศสูง ลูกบอลสีเขียวหนัก ส่วนลูกบอลสีแดงเด้งดีมาก
ดังนั้นจึงออกแบบ ตัวคัดแยก ทางกายภาพได้
อยากให้มีวิธีง่าย ๆ ในการตรวจว่ามีเครื่องที่ผมทำอันไหนเข้าไปอยู่ในเวอร์ชันสุดท้ายหรือเปล่า
ในการออกแบบครั้งหน้า ถ้าเก็บชื่อผลงานที่เคยส่งไว้ในที่อย่าง local storage แล้วแสดงการแจ้งเตือนได้ ก็น่าจะดี