5 คะแนน โดย GN⁺ 2024-08-05 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • high_impact ซึ่งปลุกโครงสร้างของเอนจิน JavaScript ชื่อ Impact จากปี 2010 กลับมาอีกครั้ง เป็นเอนจินภาษา C สำหรับเกมแอ็กชัน 2D รองรับ Windows, Mac, Linux และ WASM สำหรับเว็บ
  • Impact เป็นเอนจินที่สร้างขึ้นเพื่อแสดงให้เห็นว่า เกมเว็บก็ทำได้ด้วย Canvas2D ท่ามกลางกระแสที่ iOS ตัด Flash ออก โดยขายแบบเสียเงินราคา $99 ได้มากกว่า 3,000 ไลเซนส์ ก่อนเปิดเป็นโอเพนซอร์สฟรี
  • เอนจินใหม่เป็น เฟรมเวิร์กขนาดเล็ก ที่รวม tilemap, entity, ฟิสิกส์·การชน, แอนิเมชัน sprite, ข้อความ และเสียงไว้ด้วยกัน และใช้แบ็กเอนด์ SDL หรือ Sokol
  • การ implement ยังคงความเรียบง่ายด้วยที่เก็บ entity ขนาดคงที่, asset แบบ QOI/QOA, หน่วยความจำ hunk เดียว, renderer แบบ OpenGL·software และ level editor Weltmeister ที่ใช้ JavaScript
  • สามารถย้าย Biolab Disaster และ Drop มารันได้โดยใกล้เคียงกับซอร์ส JS เดิม และสามารถขยายไปยังหลายระบบผ่านการเพิ่มแพลตฟอร์ม·renderer

ภาพรวม high_impact

  • high_impact เป็นเอนจินเกมขนาดเล็กสำหรับเกมแอ็กชัน 2D
  • เขียนด้วยภาษา C และคอมไพล์ได้เป็น WASM สำหรับ Windows, Mac, Linux และเว็บ
  • ได้แรงบันดาลใจจากเอนจินเกม JavaScript Impact ในปี 2010 และชื่อของมันสื่อถึงยุคที่ C เคยถูกมองว่าเป็นภาษาระดับสูง
  • เผยแพร่ภายใต้ไลเซนส์ MIT และซอร์สอยู่บน GitHub

เบื้องหลังการสร้าง Impact

  • ในเดือนเมษายน 2010 Steve Jobs เผยแพร่จดหมายเปิดผนึก “Thoughts on Flash” ว่าจะไม่รองรับ Flash บน iOS
  • ในเวลานั้น Flash เป็นศูนย์กลางของวัฒนธรรมเกมเว็บและแอนิเมชันที่อิงปลั๊กอินเบราว์เซอร์ และเว็บไซต์อย่าง Newgrounds กับ Kongregate พึ่งพาคอนเทนต์ Flash อย่างมาก
  • การรองรับ Flash บน Android มีปัญหามาก และถูกประเมินว่า Adobe เองก็ไม่ได้พยายามแก้ข้อด้อยบนมือถือ
  • แม้จะมีความเข้าใจว่า “ถ้าไม่มี Flash ก็ไม่มีเกมบนเบราว์เซอร์” แต่ Canvas2D API สามารถวาดรูปภาพและรูปทรงลงบน <canvas> ได้
  • Canvas2D ถูก Apple/Safari สร้างขึ้นเพื่อใช้เรนเดอร์วิดเจ็ตเดสก์ท็อป จากนั้น Google และ Mozilla ก็รองรับตามมา ส่วน Internet Explorer ของ Microsoft ตามหลังอยู่
  • ในกระแสนี้ Biolab Disaster จึงถูกสร้างขึ้น และมีการพัฒนาเอนจินเกมกับ level editor ไปพร้อมกันเพื่อใช้กับเกมนี้

การขายและตัวอย่างการใช้งาน Impact

  • Impact ผ่านการจัดระเบียบโค้ดและทำเอกสาร ก่อนวางขายในราคา $99 แม้จะมีเสียงคัดค้านการตัดสินใจขายแบบเสียเงิน แต่ก็ขายไลเซนส์ได้มากกว่า 3,000 ชุด
  • เกมเว็บหลายเกมถูกสร้างด้วย Impact และยังถูกใช้ในเกมเชิงพาณิชย์แบบข้ามแพลตฟอร์มด้วย
  • ช่วงปลายอายุของโปรเจกต์ Impact ถูกเผยแพร่เป็นโอเพนซอร์สฟรี
  • high_impact เป็นโปรเจกต์ที่สร้าง Impact ขึ้นใหม่ตั้งแต่ต้น แต่เขียนด้วย C แทน JavaScript

ทำไมต้อง C

  • มองว่า C เป็นภาษาที่เรียบง่ายแต่ลึก คล้ายคุณสมบัติของเกมที่ “เรียนรู้ง่าย แต่เชี่ยวชาญยาก”
  • ความสนใจใน C กลับมาเพิ่มขึ้นอีกครั้งผ่านหลายโปรเจกต์
    • พอร์ตตัวถอดรหัส JavaScript MPEG1 เป็นไลบรารี single-header pl_mpeg
    • implement VR ใน Quake สำหรับ Oculus Rift
    • สร้างฟอร์แมตภาพ QOI และฟอร์แมตเสียง QOA
    • เขียน wipEout ใหม่
  • Impact เดิมไม่ได้มีขนาดพอจะเทียบกับเอนจินอย่าง Godot, Unreal หรือ Unity แต่ทำหน้าที่เป็นฐานที่มั่นคงให้หลายเกม
  • การเขียน Impact ใหม่ด้วย C เริ่มต้นขึ้นในฐานะแบบฝึกหัดที่สนุก

โครงสร้างเอนจินและ asset

  • high_impact ถูก implement ให้เรียบง่ายที่สุด และมีทิศทางให้ประกอบด้วยโค้ดน้อยที่สุดเท่าที่เป็นไปได้
  • ฟีเจอร์พื้นฐานเหมือนกับเอนจิน JavaScript เดิม
    • โหลด tilemap
    • สร้าง·อัปเดต·วาด entity ซึ่งเป็นวัตถุในเกม
    • จัดการฟิสิกส์และการชนระหว่าง entity
    • จัดการการชนกับ collision map
    • แอนิเมชัน sprite sheet
    • แสดงข้อความ
    • เล่นเอฟเฟกต์เสียงและเพลง
  • ใกล้เคียงกับ เฟรมเวิร์ก มากกว่าไลบรารี และเขียน logic ของเกมไว้ภายในเฟรมเวิร์ก
  • ด้านล่างมีแบ็กเอนด์ platform และปัจจุบันคอมไพล์ได้ด้วย SDL หรือ Sokol
  • โค้ดเกมอยู่ใน “scene” หนึ่งรายการหรือมากกว่า โดย scene เป็น struct ที่มี function pointer
    • หลังเรียก engine_set_scene(&scene_game) เอนจินจะตั้ง scene ใหม่
    • scene_game.init() ถูกเรียกหนึ่งครั้ง
    • scene_game.update() และ scene_game.draw() ถูกเรียกทุกเฟรม
  • tilemap และ entity เริ่มต้นสามารถโหลดจากไฟล์ .json หรือสร้างแบบไดนามิกได้
  • เหตุผลที่เลือก JSON เป็นฟอร์แมต level คือเพื่อ ความเข้ากันได้ย้อนหลัง กับ Impact เดิม
  • high_impact ใช้ QOI สำหรับภาพ และ QOA สำหรับเสียงกับเพลง
    • Makefile ของเกมเดโมจะแปลง PNG เป็น QOI และ WAV เป็น QOA โดยอัตโนมัติ
    • ไม่จำเป็นต้องรวมไลบรารีถอดรหัสภาพ·เสียงแยกต่างหาก
  • ในอนาคตอาจรองรับฟอร์แมต asset อื่น ๆ ได้ แต่ความเรียบง่ายของ QOI/QOA เข้ากับทิศทางของโปรเจกต์อย่างดี

ระบบ entity

  • entity ทั้งหมดแชร์ entity_t struct เดียวกัน ซึ่งบรรจุคุณสมบัติที่ high_impact ต้องใช้ เช่น ตำแหน่ง ความเร็ว และขนาด
  • entity ทุกตัวมีขนาดเป็นไบต์เท่ากัน ทำให้การเก็บและจัดการเรียบง่ายขึ้น
  • เมื่อต้องการขยับ entity ก็แค่ตั้งความเร็วหรือความเร่ง ที่เหลือเอนจินจะจัดการเอง
  • สามารถเพิ่มคุณสมบัติเฉพาะเกมให้กับ struct entity พื้นฐานได้ผ่านมาโคร
    • Biolab Disaster ใช้ union ที่มี struct แยกตามชนิด entity
    • Drop ไม่จำเป็นต้องกำหนดคุณสมบัติเพิ่ม
  • entity แต่ละชนิดต้องมี entity_vtab_t ที่ให้ function pointer
    • update ถูกเรียกทุกเฟรม
    • touch ถูกเรียกเมื่อทับซ้อนกับ entity อื่นที่ตรงตามเงื่อนไข
    • รายการทั้งหมดเป็นแบบเลือกใช้ได้
  • ที่เก็บ entity มีขนาดคงที่
    • จำนวน entity ที่ active ตามค่าเริ่มต้นคือ 1,024 ตัว
    • ตั้งค่าได้ด้วยการกำหนด ENTITIES_MAX
    • เอนจินจัดการ entity ได้สูงสุด 64k ตัวอย่างง่ายดาย
  • หากต้องเก็บ reference ไปยัง entity นานกว่าหนึ่งเฟรม ให้ใช้ entity_ref_t
    • entity_ref_t เป็น struct ที่มี uint16_t id และ index
    • สามารถตีความกลับเป็น pointer ได้ด้วย entity_by_ref()
    • สามารถแยกแยะกรณีที่มี entity อื่นเข้ามาใช้ address เดียวกันในที่เก็บได้
    • เพราะใช้ index แบบ uint16_t จำนวน entity ที่ active สูงสุดจึงถูกจำกัดที่ 64k
  • ส่วนที่ implement โครงสร้างอย่าง OOP แบบง่าย ๆ, class และ single inheritance ใน C อาจดูฝืนอยู่บ้าง แต่ high_impact พยายามทำให้ใช้งานสะดวกที่สุด
  • แนวทาง OOP แบบ “ตรงไปตรงมา” ที่รวม logic ของ entity ไว้ในที่เดียวตามชนิด เข้าใจง่ายและทำงานได้ดีในเกมที่สร้างมาจนถึงตอนนี้

การตรวจจับและตอบสนองต่อการชน

  • การชนแบบง่ายจะตรวจเพียงว่าสามารถย้ายไปตำแหน่งใหม่ได้หรือไม่ และถ้าไม่ได้ก็หยุด แต่กับวัตถุที่เร็วอาจเกิดพฤติกรรมแปลก ๆ ได้
  • ในเกมแพลตฟอร์ม 2D หากผู้เล่นอยู่เหนือพื้น 16px แล้วการเคลื่อนที่ครั้งถัดไปทำให้เข้าไปอยู่ในพื้น อาจดูเหมือน ลงพื้นอย่างนุ่มนวล โดยหยุดกลางอากาศแล้วค่อยตกลงมาอีกครั้งในเฟรมถัดไป
  • high_impact ติดตามกล่องของ entity เทียบกับ tilemap เพื่อคำนวณจุดชนที่แม่นยำ
  • วิธีนี้ซับซ้อนกว่าการตรวจแบบใช่/ไม่ใช่ธรรมดา แต่ให้ผลลัพธ์ที่ดีกว่า และยังจัดการ tile แบบลาดเอียงได้ด้วย
  • หลังชน tile แล้วอาจต้องติดตามครั้งที่สองด้วยความเร็วที่เหลืออยู่
    • ตัวอย่างเช่น เมื่อสัมผัสพื้นแบบเฉียง vel.y จะกลายเป็น 0 แต่ vel.x จะเหลือไว้เพื่อให้ไถลไปตามพื้น
  • การชนระหว่าง entity จัดการแยกต่างหาก
    • particle อาจชนกับ tilemap แต่ไม่ชนกับ entity อื่นได้
    • moving platform ชนกับ entity อื่น แต่ไม่ควรเคลื่อนที่จาก collision response
  • การตรวจจับการชนใน broad phase จะเรียง entity ตาม pos.x
    • เนื่องจากส่วนใหญ่เรียงอยู่แล้วจากเฟรมก่อนหน้า ต้นทุนของ insertion sort จึงต่ำ
    • หลังเรียงแล้วจะกวาดจากซ้ายไปขวา และตรวจเฉพาะ entity ที่อยู่ระหว่าง pos.x ถึง pos.x + size.x
  • วิธี sweep and prune นี้เร็ว ตราบใดที่ไม่มี entity จำนวนมากซ้อนกันในตำแหน่ง x ใกล้เคียงกัน
  • หาก entity จำนวนมากไปรวมอยู่ที่ตำแหน่ง x เดียวกัน เช่น หอคอยกล่องที่ซ้อนกัน จะเป็นกรณีเลวร้ายที่สุด
  • ในกรณีที่แกนอื่นเหมาะกว่า เช่น เกมยิงแนวตั้ง สามารถเปลี่ยนแกน sweep ได้ด้วย #define ENTITY_SWEEP_AXIS y

การเรนเดอร์

  • ปัจจุบัน high_impact มี renderer แบบ OpenGL และ software renderer ที่ยังไม่สมบูรณ์
  • การเรนเดอร์ทั้งหมดผ่าน API ที่บางมาก และการเรียกวาดจริงทำผ่านฟังก์ชันเดียว ทำให้การ implement แบ็กเอนด์อื่นค่อนข้างง่าย
  • ฟังก์ชันหลักที่แบ็กเอนด์เรนเดอร์เพิ่มเติมต้องรองรับคือ initialize, cleanup, ตั้งค่าขนาดหน้าจอ, เตรียม·จบเฟรม และวาด quad
  • สำหรับการจัดการ texture ยังต้องมีฟังก์ชัน mark, reset และ create
  • ฟีเจอร์เรียบง่ายมาก วาดได้เฉพาะ quad และใช้เอฟเฟกต์ shader ไม่ได้ แต่เพียงพอกับเป้าหมายของเอนจินนี้
  • software renderer มีโค้ด 140 บรรทัด และรองรับเฉพาะ quad ที่จัดแนวกับแกน
  • renderer OpenGL พยายามรวมการเรนเดอร์หนึ่งเฟรมไว้ใน OpenGL draw call เดียว
    • รวบรวม quad ทั้งหมดไว้ใน buffer ใหญ่ แล้วส่งให้ glDrawElements() ครั้งเดียว
    • รวม texture ทั้งหมดเป็น texture atlas เดียวเพื่อเลี่ยงการ bind texture ใหม่
  • texture atlas เป็นวิธีเก่าและมีข้อเสีย แต่ถูกใช้เพราะ bindless texture ยังไม่รองรับทุกที่
  • high_impact รองรับ texture atlas เดียวเท่านั้น แต่ตั้งค่าขนาดได้ด้วย #define
    • GPU มือถือมักรองรับ texture ขนาด 8k×8k
    • GPU เดสก์ท็อปรุ่นใหม่ดูเหมือนรองรับได้ถึง 32k×32k
    • Biolab Disaster และ Drop ใช้ atlas ขนาด 512×512

เสียง

  • เอาต์พุตเสียงจัดการโดย SDL2 หรือ Sokol ส่วนเอนจินรับผิดชอบการโหลด·ถอดรหัส·มิกซ์เสียงหลายชุด
  • ระบบเสียงแบ่งเป็น sound_source_t ที่มี sample และ sound_t ที่แทนเสียงซึ่งกำลังเล่นอยู่
  • ระบบนี้อิงจากระบบที่สร้างขึ้นตอนเขียน wipEout ใหม่ และสามารถคลายการบีบอัด QOA ได้เมื่อจำเป็น
  • ทุกอย่างถูกจัดสรรแบบ static
    • จำนวน source ที่โหลดได้ถูกกำหนดตายตัว
    • จำนวน sound ที่เล่นพร้อมกันได้ถูกกำหนดตายตัว
    • sound ที่เล่นจบแล้วจะถูกทิ้งอัตโนมัติและนำกลับมาใช้ใหม่
  • เสียงสามารถเปลี่ยน volume, pan ซ้ายขวา และ pitch ได้
  • หากตั้ง pitch เป็นค่าลบ เสียงจะเล่นย้อนกลับ
  • การ resampling ที่จำเป็นสำหรับ pitch แบบเปลี่ยนค่าได้ใช้วิธีคุณภาพต่ำแบบ nearest-neighbor interpolation

การจัดการหน่วยความจำ

  • high_impact มองว่า หากเกมไม่มี asset ที่ผู้ใช้สร้างเอง ก็สามารถรู้ปริมาณหน่วยความจำที่ต้องใช้ได้อย่างแม่นยำ
  • เอนจินจัดสรร byte array เดียวที่เรียกว่า “hunk” แบบ static และนี่คือหน่วยความจำทั้งหมดที่ high_impact ใช้
  • ขนาด hunk ตั้งค่าได้ด้วย #define ALLOC_SIZE
  • ใน hunk มีการจัดสรรหน่วยความจำสองรูปแบบ
    • bump allocator ที่เติบโตขึ้นจากด้านหน้า หรือ arena ใช้เก็บ asset ของเกม·entity·ข้อมูล scene ปัจจุบัน
    • temporary allocator ที่เติบโตลงจากด้านท้าย ทำงานคล้าย malloc() และ free() ใช้สำหรับพื้นที่เก็บชั่วคราว เช่น หลังถอดการบีบอัดภาพก่อนส่งให้ GPU
  • bump allocator มี “high water mark” หลายจุด และจะย้อนกลับอัตโนมัติ ณ เวลาที่กำหนด
  • หน่วยความจำที่จัดสรรแบบ bump ไม่จำเป็นต้อง free() อย่างชัดเจน
  • อายุการใช้งานเชิงแนวคิดแบ่งเป็น game, scene, frame
    • สิ่งที่จัดสรรก่อนตั้ง scene แรก จะถูกคืนเมื่อโปรแกรมจบเท่านั้น
    • สิ่งที่จัดสรรระหว่าง scene.load() จะถูกคืนเมื่อ scene จบ
    • สิ่งที่จัดสรรระหว่าง scene ทำงาน จะถูกคืนเมื่อจบเฟรม
  • load() ของ entity แต่ละชนิดถูกเรียกในขั้นตอนที่ 1 เพราะไม่รู้ล่วงหน้าว่า entity ใดจะถูกใช้ใน scene
  • allocation context เพิ่มเติมสามารถครอบด้วย alloc_pool() ได้ และภายในเป็นรูปย่อของ bump_mark() กับ bump_reset(mark)

Level editor Weltmeister

  • Impact เดิมมี level editor ชื่อ Weltmeister และ high_impact ก็รวมไว้ด้วยเช่นกัน
  • ยังคงเขียนด้วย JavaScript และใช้ซอร์สเดิมจำนวนมาก แต่ปรับให้เข้ากับฟีเจอร์เบราว์เซอร์สมัยใหม่
  • Weltmeister ทำงานแบบอิสระเต็มที่
    • ดับเบิลคลิก weltmeister.html เพื่อเริ่มสร้าง level ได้
    • ในอดีต การโหลด·บันทึกไฟล์ต้องใช้แบ็กเอนด์ API แบบ PHP หรือ NodeJS
    • ตอนนี้สามารถขอสิทธิ์เข้าถึงโฟลเดอร์เฉพาะได้ด้วย FileSystemAPI
  • Safari และ Firefox ยังไม่รองรับ showDirectoryPicker() อย่างสมบูรณ์เป็นพิเศษ จึงต้องใช้เบราว์เซอร์ตระกูล Chrome
  • Weltmeister อ่านไฟล์ซอร์ส C และรวบรวมชนิดของ entity
  • high_impact มีมาโครที่ editor เข้าใจ แต่ไม่มีผลใด ๆ ในโค้ด C
    • EDITOR_SIZE(X, Y): ขนาดใน editor ค่าเริ่มต้นคือ (8, 8)
    • EDITOR_RESIZE(RESIZE): ปรับขนาดใน editor ได้หรือไม่
    • EDITOR_COLOR(R, G, B): สีของกล่องใน editor ค่าเริ่มต้นคือ (128, 255, 128)
    • EDITOR_IGNORE(IGNORE): สร้างใน editor ได้หรือไม่

เกมเดโม

  • เพื่อยืนยันว่า high_impact ทำงานเป็นเอนจินเกมจริงได้ เกม Impact เดิม 2 เกมจึงถูกพอร์ตเป็น C
  • งานพอร์ตใกล้เคียงกับการย้ายซอร์ส JS เดิมแบบ “ถอดเสียง” และนำ asset เดิมกลับมาใช้
  • การที่มีความท้าทายน้อยถือเป็นหลักฐานได้ว่า high_impact ทำงานได้ตามตั้งใจ
  • Biolab Disaster

    • เป็นเกมเปิดตัวของ Impact เดิม
    • เป็นเกม Jump'n'Gun แบบ side-scrolling
    • ซอร์สอยู่ที่ github.com/phoboslab/high_biolab
    • เวอร์ชัน JS เดิมให้เล่นที่ playbiolab.com
  • Drop

    • เป็นเกมอาร์เคดที่เรียบง่ายมาก
    • ซอร์สอยู่ที่ github.com/phoboslab/high_drop
    • เวอร์ชัน JS เดิมอยู่ที่ impactjs.com/drop/
    • ปัจจุบันมีการนำเสนอคะแนนสูงสุดเป็น 26789 Points

ความสามารถในการขยาย

  • high_impact มีโครงสร้างที่เขียนโค้ดเฉพาะเกมแบบ เพิ่มเข้าไป เหมือนเอนจินเกมแบบดั้งเดิม
  • ไม่จำเป็นต้องแก้ซอร์สเอนจิน แต่มีทิศทางให้เรียบง่ายพอที่จะปรับซอร์สเอนจินโดยตรงได้หากจำเป็น
  • แพลตฟอร์มและ renderer ถูกออกแบบให้ขยายได้โดยไม่ต้องแก้โค้ดส่วนอื่น
  • หากสนใจ ยินดีรับ pull request สำหรับรองรับ renderer อย่าง Vulkan, DirectX, Metal และแบ็กเอนด์แพลตฟอร์มอย่าง PSX, N64, Dreamcast
  • เพราะเขียนด้วย C จึงมีทิศทางว่าควรรันได้ทุกที่

1 ความคิดเห็น

 
GN⁺ 2024-08-05
ความคิดเห็นจาก Hacker News
  • งานเขียนโปรแกรมจำนวนไม่น้อยที่ผมได้เรียนรู้มากที่สุดล้วนเป็นเพราะ Impact
    Impact ล้ำยุคมากจริง ๆ และผมภูมิใจที่ได้เป็นหนึ่งในผู้ถือไลเซนส์ 3,000 คน มันเป็นการซื้อที่ดีที่สุดครั้งหนึ่งของผม และเกมเดียวที่ผมทำจนเสร็จสมบูรณ์จริง ๆ ก็สร้างด้วย Impact
    ผมชอบที่ไลเซนส์มี ซอร์สโค้ด รวมอยู่ด้วย และผมก็แก้ทั้งเอนจินกับเอดิเตอร์เองให้ตรงกับความต้องการ ผลจากเรื่องนั้นทำให้ผมใช้เวลาหลายปีสร้างเอนจินเกม JS ของตัวเอง แต่กลับเลื่อนการทำเกมให้เสร็จออกไป ถึงอย่างนั้นก็ได้เรียนรู้มากมายในกระบวนการนั้น และทำเกมสำหรับเกมแจมออกมาเยอะมาก
    ผมยังได้รับแรงบันดาลใจจาก Ejecta ซึ่งเป็นการรองรับ iOS แบบเนทีฟของ Impact ด้วย แต่ตอนนั้นหงุดหงิดที่มันรันบน Android ไม่ได้ เลยสร้าง JVM binding สำหรับ V8 และ implement บางส่วนของ WebGL เพื่อให้เอนจินของผมรันบน Android ได้โดยไม่ต้องใช้เว็บวิว รีโพซิทอรี V8 binding ที่ผมเผยแพร่ดันถูกนำไปใช้ในซอฟต์แวร์เชิงพาณิชย์อย่างไม่คาดคิด: https://github.com/namuol/jv8
    ผมถึงขั้นพยายามทำสตาร์ทอัพแบบบูตสแตรปที่ขายสิทธิ์เข้าถึง GitHub repository แบบส่วนตัว โดยได้แรงบันดาลใจจากโมเดลธุรกิจของ Impact แต่เรื่องนั้นคงยาวไปหน่อย อย่างไรก็ดี พอเห็น Impact ถูกอัปเดตให้เข้ากับเว็บ “ยุคใหม่” ด้วยการพอร์ตไป C แล้วก็รู้สึกอบอุ่นใจและสนุกดี อยากจะบอกว่านี่เป็นช่วงเวลาแปลก ๆ ของเว็บ แต่ผมนึกไม่ออกเลยว่าเคยมีช่วงไหนที่เว็บไม่แปลก

    • เดิมที Impact เป็น เอนจินที่ใช้ JavaScript และเบราว์เซอร์ เลยนึกว่าบน Android ก็น่าจะรันได้ดีด้วยเว็บวิวธรรมดาไม่ใช่หรือ
  • CrossCode เป็นเกมที่ยอดเยี่ยม ผมรู้ว่ามันใช้เทคโนโลยีเว็บ และยังทึ่งอยู่เรื่อย ๆ ว่ามันทำประสิทธิภาพได้ดีขนาดนั้นบนฮาร์ดแวร์ Nintendo Switch
    เอนจินนี้ก็น่าจะมีส่วนช่วยอยู่พอสมควร

    • ถ้าพูดให้แฟร์ ทีม CrossCode แก้ Impact ไปเยอะมากจริง ๆ ถ้าดูสตรีมพัฒนาบางส่วน จะเห็นว่า Weltmeister ซึ่งเป็น level editor ของ Impact ถูกขยายไปมาก: https://youtu.be/4lZfnM9Ubeo?t=3215
      ตรงนี้แหละที่เจ๋งกว่าเดิม ผมชอบที่นักพัฒนาสามารถปรับเอนจินให้เหมาะกับเกมของตัวเองได้ และในทำนองเดียวกัน high_impact ก็ควรถูกมองว่าเป็นจุดเริ่มต้นที่สะดวก มากกว่าเป็นเอนจินเกมแบบ “ฟีเจอร์ครบถ้วน”
    • การพอร์ตลง Switch อย่างที่มีคนพูดไปแล้ว ใช้ความพยายามมาก และต่างจาก impact.js มาตรฐานโดยสิ้นเชิง
      เกร็ดที่น่าสนใจคือ ทุกคนอยากได้เวอร์ชัน Switch แต่เพราะข้อจำกัดทางเทคนิค ทีมจึงตอบว่า “เมื่อ Hedgehags เรียนรู้วิธีบินได้ CrossCode ก็จะออกบน Switch”: https://www.radicalfishgames.com/?p=6581
      สุดท้ายเมื่อพอร์ตสำเร็จ ก็มีเควสต์เสริมชื่อ “A switch in attitude” เพิ่มเข้ามา และแน่นอนว่ามี hedgehags บินได้ปรากฏตัวด้วย: https://www.radicalfishgames.com/?p=6668
    • มีทอล์กเกี่ยวกับกระบวนการพอร์ต CrossCode ไป Switch: https://www.youtube.com/watch?v=KfBzlzvt8RU
    • เท่าที่จำได้ การพอร์ตลง Switch ต้องใช้ความพยายามในระดับ วีรกรรม เลย
  • “Thoughts on Flash” อาจช่วยกอบกู้เว็บไว้ในจังหวะที่แพลตฟอร์มเว็บต้องการมากที่สุด นั่นคือช่วงที่ การครอบงำของซอฟต์แวร์ตัวเดียว ค่อย ๆ ขยายตัวขึ้น
    ในบทความนั้นก็น่าจะมีความไม่พอใจต่อ Adobe ที่ดูเหมือนละเลยการรองรับ MacOS เพราะให้ความสำคัญกับฐานผู้ใช้ Windows ที่ใหญ่กว่ามาก เช่น เวอร์ชัน Mac มักตามหลังเวอร์ชัน Windows เสมอ
    Jobs อาจรู้สึกว่าในเมื่อ Apple ทำให้ Adobe เป็นไปได้ Adobe ก็ควรตอบแทน Apple ด้วยเหมือนกัน แต่นั่นก็ออกจะเป็นการคาดเดา ตัวเกมเองดูเนียนมากจริง ๆ

    • บน PSP ก็มี Flash ด้วย และมันค่อนข้างดีทีเดียว สงสัยว่า Sony ทุ่มความพยายามกับเงินไปกับตรงนั้นมากแค่ไหน
    • อ่านบทความนี้แล้วทำให้ผมกลับไปลองจับ C อีกครั้ง เดิมทีผมเป็นสาย ECMAScript และเคยทำ Lingo นิดหน่อยเมื่อนานมาแล้ว แต่ผมถูกดึงดูดด้วยสิ่งที่ใช้ทรัพยากรต่ำและใกล้ฮาร์ดแวร์
      แต่ก็ไม่ได้ใกล้ถึงระดับภาษาแอสเซมบลี ผมกำลังลองลดทอนสิ่งต่าง ๆ เพื่อให้มันพาผมขุดลึกลงไปมากขึ้น
  • ในฐานะคนที่อยากหลุดจากงานบริษัทแล้วสักวันหนึ่งไปลุยไซด์โปรเจกต์อย่างจริงจัง ผมอยากฟังเพิ่มเติมเกี่ยวกับ ส่วนที่ยืนได้ด้วยการเก็บเงิน
    ความคิดเรื่องการรับเงินจากงานที่เดิมทีทำเพื่อความสนุกมันทำให้รู้สึกหนักใจแปลก ๆ แต่ผมก็รู้ว่ามันอาจทำให้ผมได้ทำสิ่งที่ชอบแบบเต็มเวลา

    • ถ้าเป็นงานดี ๆ ที่แก้ปัญหาให้ใครสักคนได้ การได้รับค่าตอบแทนก็สมเหตุสมผลอย่างยิ่ง และคุณก็คงรู้อยู่แล้ว
      ดังนั้นสิ่งสำคัญคือการหาว่าทำไมความคิดนั้นถึงทำให้รู้สึกหนักใจ เหตุผลที่พบบ่อยก็เช่น คนรอบตัวคอยห้ามบ่อย ๆ, ยังขาดทักษะที่จะลงมือทำสิ่งที่อยากทำให้ดี, รู้สึกอายที่จะขอความช่วยเหลือหรือกลัวว่าจะรบกวนอีกฝ่าย, กลัวว่างานของตัวเองจะถูกประเมิน, หรือกลัวสูญเสีย “ความปลอดภัย” จากรายได้ปัจจุบัน โดยเฉพาะเมื่อมีครอบครัวที่ต้องดูแล
      ส่วนใหญ่แล้วเหตุผลเหล่านั้นไม่ใช่เหตุผลที่ดีมากนัก แต่ใกล้เคียงกับการที่ต้องปรับตัวใหม่ในระดับหนึ่ง และมันให้ความรู้สึกเหมือน “ความเสี่ยง” จนทำให้ออกจาก comfort zone ได้ยาก ถ้ามีกรอบคิดแบบนั้น ทุกโอกาสจะดูเหมือนความเสี่ยง จึงยากมากที่จะหาจังหวะเหมาะ ๆ ในการเริ่มทำสิ่งที่อยากทำจริง ๆ
      ที่เกี่ยวข้องกันคือ การทำสิ่งที่คิดว่าสนุกแล้วเอาออกไปให้โลกเห็นเป็นเรื่องสำคัญ แต่การเปลี่ยนสิ่งนั้นให้กลายเป็นงานเลี้ยงชีพเป็นความท้าทายคนละเรื่องกันเลย คนส่วนใหญ่ไม่สามารถทำสิ่งที่รักให้เป็นอาชีพได้ และต่อให้ทำได้ ความคาดหวังของลูกค้าที่จ่ายเงินกับแรงกดดันในการรักษารายได้ก็อาจพรากความรักนั้นไปได้ ไม่ได้หมายความว่าอย่าทำ แค่เป็นสิ่งที่ควรรู้ก่อนกระโจนเข้าไป
  • ถึงขั้นล็อกอินเข้า HN ด้วยบัญชีที่แทบไม่ใช้ เพราะเมื่อหลายปีก่อนผมเล่น Biolab Disaster ซ้ำแล้วซ้ำเล่า แต่ลืมชื่อไปแล้ว
    รู้สึกน่าทึ่งทีเดียวที่บังเอิญได้เจอมันอีกครั้ง

  • ปกติผมจะอธิบายเรื่องนี้ในเชิงลบกว่านี้มาก เพราะมองว่า “เฟรมเวิร์ก” ก็เป็นแค่ ไลบรารีที่เข้ากับสิ่งอื่น ๆ ได้ไม่ดี
    ถึงอย่างนั้น พอได้ยินคำอธิบายเชิงบวกที่ฟังสมเหตุสมผลแบบนี้ก็รู้สึกดี

    • คำอธิบายที่ผมเคยได้ยินคือ ไลบรารีคือสิ่งที่ผมเรียกใช้ ส่วน เฟรมเวิร์กคือสิ่งที่เรียกใช้ผม
    • ผมเรียนวิศวกรรมซอฟต์แวร์มา แต่ก็ยังไม่เข้าใจความต่างระหว่างไลบรารีกับเฟรมเวิร์กจริง ๆ จนกระทั่งได้พัฒนาด้วย WebObjects ในงานแรก
      การใช้เฟรมเวิร์กที่ครบถ้วนและออกแบบมาดี ซึ่งจัดการ 99% ของสิ่งที่ต้องการได้อย่างยอดเยี่ยมนั้นสนุกมาก การเพิ่มโค้ดของผมเข้าไปก็ง่ายที่สุดครั้งหนึ่งในบรรดาการพัฒนาที่เคยทำมา และมันก็ทำงานได้ดีจริง ๆ ราวกับเวทมนตร์ และผมก็ยังคิดถึงมันอยู่
    • ผมยังคิดว่ามันมีด้านลบอยู่
      เฟรมเวิร์กในอุดมคติของผม ภายในควรเป็นไลบรารีหรือชุดไลบรารีที่ทำงานร่วมกัน และมีความเป็นเฟรมเวิร์กให้น้อยที่สุดเท่าที่เป็นไปได้
      เช่น Qt เป็นเฟรมเวิร์ก และ Qt ก็ “เรียกใช้ผม” แต่เราก็สามารถรันโค้ด QPainter ได้โดยไม่ต้องเริ่ม event loop ของ Qt หรือคิดลึกเรื่อง QObject ตามอุดมคติแล้ว เราควรใช้ event loop ได้โดยไม่ต้องยอมรับสัญญาณกับสล็อตทั้งหมดด้วย เพียงแต่อาจใช้งานไม่ลื่นเท่า
      มันไม่ได้เป็นไปได้เสมอไป และไม่ได้คุ้มค่าเสมอไป แต่ถ้าเงื่อนไขอื่นเท่ากัน ผมจะชอบแบบที่ไม่มีเฟรมเวิร์กเลยมากกว่า
      สำหรับเกมเอนจิน ผมเข้าใจว่าทำไมต้องมีความเป็นเฟรมเวิร์กอยู่บ้าง เมื่อคอมไพล์ไปยังแพลตฟอร์มเฉพาะอย่างมือถือหรือคอนโซล เอนจินต้องเข้าไปเกี่ยวข้องกับกระบวนการ build และบางครั้งแม้กระทั่งส่วนที่เกี่ยวกับ libc ด้วย ดังนั้นจะสร้างแค่ไฟล์ executable แบบ Win32 แล้วเรียกว่าเป็นเกม PlayStation ไม่ได้
  • เมื่อนำ 7Zip มารวมกับ ฟอร์แมตไฟล์ไร้การสูญเสีย QOI จะได้ประสิทธิภาพดีกว่า PNG แบบไร้การสูญเสีย เป็นงานที่น่าทึ่งมาก

    • ถ้าเอา 7Zip ไปใช้กับ BMP ก็น่าจะดีกว่าเช่นกัน และความต่างอาจมากกว่าด้วย สุดท้ายแล้วมันก็ลงเอยที่ความต่างระหว่าง gzip กับ ตัวบีบอัดทดแทน gzip
  • “for No Reason” อาจเป็นเพราะต้องการเคารพ อายุแบตเตอรี่ ของผู้เล่นก็ได้

  • ผมชอบส่วนการจัดการหน่วยความจำ การจัดสรรแบบ arena นั้นเรียบง่ายจริง ๆ
    เว็บเซิร์ฟเวอร์ของเล่นที่ผมใช้ก็เริ่มจาก arena เหมือนกัน แต่ไม่นานก็รู้ตัวว่าไม่จำเป็นต้องเพิ่มหรือลดหน่วยความจำเลย
    ตอนนี้ผมจัดสรรหน่วยความจำทั้งหมดที่ต้องใช้ตั้งแต่ต้น แล้วแบ่งเป็นชิ้น ๆ ให้แต่ละโมดูลใช้ เวลาเขียนโปรแกรมเรามักสมมติว่าอาจต้องใช้หน่วยความจำปริมาณเท่าไรก็ได้ แต่จริง ๆ ไม่จำเป็นต้องเป็นอย่างนั้น
    หลายอย่างมีขีดจำกัดที่ชัดเจนจริง ๆ และที่เหลือส่วนใหญ่ก็มักกำหนดขีดจำกัดได้ เมื่อไล่แจกแจงออกมา เราก็จะรู้ว่าต้องใช้หน่วยความจำเท่าไร การคิดและกำหนดขีดจำกัดแบบนั้นล่วงหน้านั้นสนุก ทำให้มั่นใจ และช่วยปลูกฝังนิสัยประหยัดที่ดี

    • ผมมักหงุดหงิดเสมอเวลามีคนบอกว่า performance ของ std::vector แย่มาก โดยเฉพาะในเกม
      ถ้าเริ่มจากเวกเตอร์ว่าง ๆ แล้วเพิ่มรายการเป็นพัน ๆ โดยไม่ reserve หน่วยความจำไว้ก่อน แน่นอนว่ามันอาจเป็นแบบนั้น แต่ในหลายกรณีมาก เราสามารถหาค่าสูงสุดที่ต้องใช้จริงและจัดสรรล่วงหน้าได้ แล้วทุกอย่างก็โอเค
    • ฐานข้อมูล TigerBeetle ถูกสร้างด้วยแนวทางนี้ และผมค่อนข้างชอบแนวคิดนี้: https://tigerbeetle.com/blog/a-database-without-dynamic-memo...
  • ผมชอบมากกับวิธีสร้างโครงสร้างข้อมูล ENTITY ที่เป็นชนิดแบบ polymorphic ด้วย union ออกแบบได้ดี
    ผมยังชอบเล่นกับ C อยู่ มันเป็นภาษาแรกที่ผมเรียน และผมก็ลำบากกับมันอยู่หลายปี อย่างที่ต้นฉบับบอก C เป็นภาษาที่กระชับจึงยอดเยี่ยม และถ้าต้องการก็ลงลึกได้มากเท่าที่อยาก
    เกมนี้ให้บรรยากาศเหมือน Commander Keen สมัยก่อน ซึ่งผมชอบ และครั้งหนึ่งผมก็เคยค่อนข้างชอบแฟรนไชส์ที่ Carmack ทำในยุคก่อน 3D นั้น