18.2 Game Jam

เฟส 18 · พอร์ตโฟลิโอ, อาชีพ และสัมภาษณ์ · เวลาเรียน: ongoing

สร้างเกมทั้งเกมในสุดสัปดาห์เดียว — ทางที่เร็วที่สุดในการเรียนเรื่อง scope, การทำให้จบ และทีมเวิร์ก และเติมพอร์ต

คุณน่าจะเคยเริ่มโปรเจกต์เกมมาห้าโปรเจกต์ แล้วทำเสร็จไปศูนย์โปรเจกต์เลย นั่นคือแพทเทิร์นปกติของมือใหม่ และเป็นเหตุผลใหญ่ที่สุดข้อเดียวที่ทำให้มือใหม่ยังคงเป็นมือใหม่อยู่ตลอด: การทำเกมให้เสร็จเป็นทักษะที่แตกต่างจากการเริ่มทำเกมโดยสิ้นเชิง และคุณจะเก่งขึ้นได้ก็ต่อเมื่อทำมันภายใต้ deadline จริง ๆ เท่านั้น game jam คือที่ที่คุณจะได้ฝึกการทำให้เสร็จ บทนี้จะพูดถึงว่า jam จริง ๆ แล้วคืออะไร jam หลัก ๆ ที่คุณเข้าร่วมได้ วิธีตัด idea ใหญ่ ๆ ให้เหลือแค่สิ่งที่สร้างได้ในหนึ่งสุดสัปดาห์ แผนงานแบบชั่วโมงต่อชั่วโมงสำหรับ 48 ชั่วโมง ลำดับการสร้างสิ่งต่าง ๆ สิ่งที่ต้องตัดทิ้งเมื่อเวลาใกล้หมด วิธีใช้ asset ฟรีโดยไม่ผิดกฎ วิธีเตรียมตัวล่วงหน้าเพื่อไม่ให้เสียเวลา jam ไปกับ boilerplate และหน้าตาของการ submit ที่ดีควรเป็นยังไง

1. Game Jam คืออะไร?

game jam คืออีเวนต์แบบ timeboxed (มีเวลาเริ่มตายตัวและเวลาจบตายตัว ไม่มีต่อเวลา) ที่คุณต้องสร้างเกมจากศูนย์จนถึงสถานะที่ submit ได้และเล่นได้จริงภายในกรอบเวลานั้น ความยาวที่พบบ่อยที่สุดคือ 48 ชั่วโมง (หนึ่งสุดสัปดาห์) หรือ 72 ชั่วโมง (สุดสัปดาห์ยาว) พอถึงเวลาเริ่มพอดี ผู้จัดจะประกาศ theme (ธีม) — คำหรือวลีสั้น ๆ เช่น "Roles Reversed" หรือ "รวมสองแนวเกมที่ไม่น่าจะเข้ากันได้" — แล้วผู้เข้าร่วมทุกคนต้องเชื่อมเกมของตัวเองเข้ากับธีมนั้นไม่ทางใดก็ทางหนึ่ง ตามที่ตัวเองตีความ

Fri 7:00 PM Sun 7:00 PM | | |------------------ 48 hours ------------------------| theme submission revealed deadline

ไม่มีใครรู้ธีมล่วงหน้า นั่นคือประเด็นสำคัญ: คุณสร้างเกมไว้ล่วงหน้าเป็นเดือน ๆ แล้วแปะธีมทับตอนท้ายไม่ได้ (เอาจริงก็มีคนพยายามทำแบบนั้นเหมือนกัน แต่มันมักจะดูออก) คุณเริ่มจากศูนย์จริง ๆ — ดีไซน์เป็นศูนย์ อาร์ตเป็นศูนย์ โค้ดเป็นศูนย์ ยกเว้น template ที่ reuse ได้ที่คุณเตรียมไว้เองล่วงหน้า ซึ่งหัวข้อ 5 จะพูดถึง

หลัง deadline jam ส่วนใหญ่จะมี rating period (ช่วงให้คะแนน) ที่แยกออกมาต่างหากและยาวกว่า — เป็นวันหรือสัปดาห์ที่ผู้เข้าร่วมทุกคนไปเล่นและให้คะแนนเกมของคนอื่น หัวข้อ 11 จะพูดถึงว่าต้องทำอะไรบ้างในช่วงนั้น

2. ทำไม "การทำให้เสร็จ" คือทักษะที่ Jam สอนคุณ

ลองเปิดโฟลเดอร์โปรเจกต์ของตัวเองดูตอนนี้เลย ถ้ามี มันน่าจะหน้าตาประมาณนี้:

My Projects folder, one year in: RPG_Engine_v2 40% done, abandoned month 2 Platformer_Big_Idea 15% done, abandoned week 3 MMORPG_Concept 5% done, abandoned week 1 Roguelike_Deckbuilder 60% done, abandoned month 4 Total FINISHED, shippable games: 0

นี่ไม่ใช่ความล้มเหลวส่วนตัวอะไรเลย มันคือสิ่งที่เกิดขึ้นเป็นค่าเริ่มต้นเมื่อโปรเจกต์ไม่มี deadline และไม่มีคนดู ถ้าไม่มี deadline "เพิ่มอีกฟีเจอร์เดียวก่อน" จะมีให้ทำได้เรื่อย ๆ ตลอดไป และไม่มีแรงกดดันจากภายนอกให้ต้องประกาศว่าเสร็จสักที ถ้าไม่มีคนดู ก็ไม่มีใครสังเกตด้วยว่าคุณปล่อยให้มันตายเงียบ ๆ ไปตั้งแต่เดือนที่สอง

Jam ตัดข้ออ้างทั้งสองอย่างนี้ออกไปพร้อมกัน deadline เป็นเรื่องจริงและเปิดเผยต่อสาธารณะ — หน้า submit ปิดตรงเวลาเป๊ะ ไม่มีข้อยกเว้น คนดูก็เป็นเรื่องจริงเหมือนกัน — ผู้เข้าร่วม jam คนอื่น ๆ จะเปิดเกมของคุณเล่นจริงและให้คะแนนจริง ซึ่งแปลว่า "ทำให้เสร็จ ไม่งั้นไม่มีใครได้เห็นเลย" ไม่ใช่แค่สมมติฐาน ภายใต้แรงกดดันนี้ คุณถูกบังคับให้ฝึกทักษะที่งานสตูดิโอจริงต้องใช้ทุกวัน: ตัดสินใจว่าอะไรจำเป็นจริง ๆ กับอะไรแค่ดีถ้ามี ตัดฟีเจอร์ทิ้งภายใต้ deadline ส่งของที่ไม่สมบูรณ์ตรงเวลาแทนที่จะรอของสมบูรณ์แบบที่ไม่มีวันเสร็จ และแพ็กเกจ build ให้คนแปลกหน้ารันได้จริง ไม่มีอะไรในนี้จะโผล่มาให้เห็นถ้าคุณมีเวลาไม่จำกัดในโปรเจกต์เดี่ยว แต่ทั้งหมดนี้จะโผล่มาให้เห็นภายใน 48 ชั่วโมง

Tip เป้าหมายของคุณใน jam ครั้งแรก ไม่ใช่การทำเกมที่ดี แต่คือการ submit อะไรก็ได้ ตรงเวลา ที่รันได้จริง คุณภาพจะตามมาใน jam ครั้งที่สองและสาม พอความรู้สึกกลัวการทำไม่เสร็จหายไป มือใหม่ที่ไล่ล่าความ "ดี" ใน jam ครั้งแรกมักจะหมดเวลาแล้ว submit อะไรไม่ได้เลย ซึ่งไม่สอนอะไรคุณเลย เพราะบทเรียนทั้งหมดอยู่ในการกระทำของการทำให้เสร็จ

3. Jam หลัก ๆ และวิธีเข้าร่วม

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

Jam Length When Team allowed -------------------------------------------------------------------- Ludum Dare Compo 48 hours 3 times a year no, solo only Ludum Dare Jam 72 hours same weekend as Compo yes Global Game Jam 48 hours one weekend, January yes GMTK Game Jam 48 hours one weekend, summer yes itch.io community varies year-round, thousands varies

การเข้าร่วมแต่ละที่ก็ขั้นตอนพื้นฐานเดียวกัน: สร้างบัญชีฟรีบน itch.io เข้าไปที่ itch.io/jams หา jam ที่กำลังเปิดรับสมัครอยู่ แล้วกด "Join Jam" ก่อนงานเริ่ม พอถึงเวลาเริ่ม ธีมจะถูกประกาศบนหน้า jam นั้น คุณสร้างเกมไปตลอดระยะเวลาของ jam แล้ว submit หน้าเกมของคุณ (พร้อม build ที่เล่นได้แนบมาด้วย) ก่อน deadline หลาย jam ยังคาดหวัง — หรือถึงขั้นบังคับ — ให้คุณไปให้คะแนนเกมของคนอื่นสักสองสามเกมหลังจากนั้น ซึ่งหัวข้อ 11 จะพูดถึง

4. Scope คือเกมทั้งเกม: ตัด Idea ให้เหลือแค่ขนาดสุดสัปดาห์

Scope หมายถึงทุกอย่างที่สิ่งที่เสร็จแล้วต้องมีจริง ๆ — ไม่ใช่แค่ "ฟีเจอร์" แต่รวมถึงหน้าเมนูทุกหน้า อาร์ตทุกชิ้น เสียง effect ทุกเสียง ด่านทุกด่าน บั๊กทุกตัวที่ต้องแก้ และตัว build ที่ export ออกมาด้วย มือใหม่มักคิดว่า scope คือ "กลไกเจ๋ง ๆ ที่ฉันอยากได้" แล้วลืมไปว่าเมนูหลักที่ใช้งานได้จริง วิธีแพ้เกม และไฟล์ .exe ที่ export แล้วไม่ crash ก็เป็นส่วนหนึ่งของ scope เหมือนกัน

นี่คือกฎที่มีประโยชน์ที่สุดข้อเดียวสำหรับ jam: idea แรกของคุณใหญ่เกินไปเสมอ นี่ไม่ใช่แค่ "อาจจะ" — มันเกือบจะเป็นกฎธรรมชาติไปแล้ว ทันทีที่ธีมถูกประกาศ สมองคุณจะตื่นเต้นและจินตนาการถึงเวอร์ชันที่ทะเยอทะยานที่สุดของเกมรอบธีมนั้น ไม่ใช่เวอร์ชันขั้นต่ำที่สร้างได้จริง idea แรกของทุกคน ทุก jam ต้องถูกตัดลง มักจะมากกว่าหนึ่งครั้งด้วยซ้ำ

นี่คือตัวอย่างที่ลงมือทำจริงของกระบวนการตัดนี้ เริ่มจาก idea ที่ใหญ่เกินไปจริง ๆ:

v1 - the weekend-one dream: "Open-world RPG: build a village, fight monsters, romance NPCs, a procedurally generated world, a day/night cycle, a full crafting system, multiplayer co-op" | | cut: no open world, no multiplayer, no romance system v v2 - still too big: "One village hub plus one procedurally generated dungeon, 5 monster types, craft 10 items" | | cut: no crafting, one hand-built dungeon instead of | procedural generation, one monster type v v3 - buildable in a weekend: "One hand-built arena. One monster type that spawns in waves. The player has one attack and one dodge. Survive as long as possible. Score = seconds survived."

สังเกตว่าอะไรรอดมาถึง v3: ยังมี player action ที่ชัดเจน (โจมตี หลบ) มีภัยคุกคามที่ชัดเจน (มอนสเตอร์เป็นเวฟ) มีกรอบแพ้/ชนะที่ชัดเจน (รอดให้นานที่สุด) และมี hook คะแนนที่ชัดเจน (วินาทีที่รอดชีวิต ซึ่งใครก็ตามที่มาให้คะแนนอ่านเข้าใจทันที) สิ่งที่ถูกตัดออกคือทุกอย่างที่ทำให้ปริมาณเนื้อหาที่ต้องสร้างเพิ่มขึ้นทวีคูณ — หมู่บ้านทั้งหมู่บ้าน ไอเทม craft สิบชิ้น ตัว generator แบบ procedural — โดยไม่ได้ทำให้ 10 วินาทีแกนกลางของ gameplay สนุกขึ้นเลย

กระบวนการที่ทำซ้ำได้: เขียน idea ของคุณลงเป็นประโยคเดียว สำหรับทุกคำนามและคำกริยาในประโยคนั้น ถามว่า "ตัดสิ่งนี้ออกแล้วยังเป็นเกมอยู่ไหม?" ตัดไปเรื่อย ๆ จนกว่าจะตัดอะไรเพิ่มไม่ได้แล้วโดยไม่เสีย "ความเป็นเกม" ไปทั้งหมด — พูดให้ชัดคือ จนกว่าการตัดอีกสิ่งเดียวจะทำให้เงื่อนไขชนะ เงื่อนไขแพ้ หรือ input แกนกลางหนึ่งอย่างที่ผู้เล่นต้องเรียนรู้หายไป

Tip เช็ค scope แบบเร็ว ๆ: idea ที่ตัดแล้วของคุณมี (1) วิธีแพ้ (2) วิธีชนะหรือระบบคะแนน และ (3) input แกนกลางที่ผู้เล่นต้องเรียนรู้แค่หนึ่งอย่างพอดีไหม? ถ้าตอบ "ใช่" ได้ทั้งสามข้อโดยใช้แค่สิ่งที่เหลืออยู่หลังตัด แปลว่าคุณมี scope ที่สร้างได้จริง แต่ถ้าคำตอบข้อไหนคือ "เอาจริงเดี๋ยวมันก็จะมี" ให้ตัดเพิ่มอีก

5. เตรียม Template Project ที่ Reuse ได้ก่อน Jam เริ่ม

48 ชั่วโมงของ jam มีไว้สำหรับสร้างส่วนที่เป็นของใหม่จริง ๆ ใน idea ของคุณ ไม่ใช่เอาไว้แก้ปัญหาที่คุณเคยแก้ไปแล้วในโปรเจกต์ก่อนหน้า — วิธีสลับ scene วิธีเล่นเสียง effect วิธีอ่าน input จากผู้เล่น ทุกชั่วโมงที่เสียไปกับการสร้าง boilerplate พวกนี้ใหม่ระหว่าง jam คือชั่วโมงที่ไม่ได้ใช้กับกลไกแกนกลางของคุณ ทางแก้ง่ายมาก: สร้าง template project เล็ก ๆ ในเวลาว่างของตัวเอง ก่อน jam ไหนจะเริ่ม แล้ว copy มันเป็น commit แรกสุดของทุกโปรเจกต์ jam ที่คุณทำ

อย่างน้อยที่สุด template ของ jam ควรแก้ปัญหาสามอย่างนี้ไว้แล้ว: อ่าน input ของผู้เล่นในที่เดียว โหลด scene และเล่นเสียง นี่คือจุดเริ่มต้นกระชับ ๆ สำหรับแต่ละอย่าง โดยใช้แพทเทิร์นที่เรียกว่า singleton (คลาสที่รับประกันว่ามีอินสแตนซ์ของตัวเองอยู่แค่ตัวเดียว และเรียกใช้ได้จากทุกที่ผ่าน static field ชื่อ Instance — มีประโยชน์ตรงนี้เพราะคุณอยากได้ audio manager แค่ตัวเดียวและ scene loader แค่ตัวเดียวสำหรับทั้งเกม ไม่ใช่ตัวละหนึ่งต่อ scene)

using UnityEngine;

// One place that reads raw input, so gameplay code never calls
// Input.GetAxis directly. Swap the inside of this file for a new
// control scheme without touching any other script in the jam.
public class InputReader : MonoBehaviour
{
    public static InputReader Instance { get; private set; }

    public Vector2 Move { get; private set; }
    public bool JumpPressed { get; private set; }

    void Awake()
    {
        Instance = this;
    }

    void Update()
    {
        Move = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
        JumpPressed = Input.GetButtonDown("Jump");
    }
}
using UnityEngine;
using UnityEngine.SceneManagement;

// Put this on an empty GameObject in a "Bootstrap" scene that loads
// first. It survives every later scene load, so any script anywhere
// can call SceneLoader.Instance.LoadScene(...) without a reference.
public class SceneLoader : MonoBehaviour
{
    public static SceneLoader Instance { get; private set; }

    void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
        DontDestroyOnLoad(gameObject);
    }

    public void LoadScene(string sceneName)
    {
        SceneManager.LoadScene(sceneName);
    }
}
using UnityEngine;

// Also lives in the Bootstrap scene. Anything in the jam game can
// call AudioManager.Instance.PlaySfx(clip) starting hour 1, instead
// of you rebuilding an audio system at hour 30.
public class AudioManager : MonoBehaviour
{
    public static AudioManager Instance { get; private set; }

    public AudioSource musicSource;
    public AudioSource sfxSource;

    void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
        DontDestroyOnLoad(gameObject);
    }

    public void PlaySfx(AudioClip clip)
    {
        sfxSource.PlayOneShot(clip);
    }

    public void PlayMusic(AudioClip clip)
    {
        if (musicSource.clip == clip) return;
        musicSource.clip = clip;
        musicSource.loop = true;
        musicSource.Play();
    }
}

ลองไล่ trace guard clause ใน SceneLoader และ AudioManager ดู: สมมติว่าคุณเผลอทิ้งสำเนาที่สองของ Bootstrap object ไว้ใน scene Level1 ของคุณ จากการทดสอบครั้งก่อน พอ Level1 โหลดทับ Bootstrap scene จริง Unity จะเรียก Awake บน AudioManager ทั้งสองตัว ตัวแรก (ตัวจริง ที่เป็น Instance อยู่แล้ว) เจอว่า Instance != null && Instance != this เป็น false สำหรับตัวมันเอง เลยทำงานต่อตามปกติ ตัวที่สอง (สำเนาที่เผลอทิ้งไว้ใน Level1) เจอว่า Instance ถูกตั้งเป็นอ็อบเจกต์อื่นไปแล้ว เงื่อนไขเลยเป็นจริง แล้วมันก็ทำลายตัวเองทันที ผลลัพธ์: มี AudioManager รอดอยู่แค่ตัวเดียวเสมอ ไม่ว่าจะมีสำเนาเผลอโหลดมากี่ตัวก็ตาม และคุณจะไม่ได้ยินเพลงซ้อนกันสองเพลงเลย ถ้าไม่มี guard clause นี้ ทั้งสองตัวจะรอด แล้วคุณจะเจอบั๊กแบบนั้นเป๊ะ — ซึ่งก็คือ Exercise 3 ท้ายบทนี้ด้วย

Tip เก็บ template นี้ไว้ในโฟลเดอร์หรือ repository ส่วนตัวเล็ก ๆ แยกออกจากโปรเจกต์ jam แต่ละครั้ง ก่อน jam ทุกครั้ง copy เวอร์ชันสดใหม่แทนที่จะเอาโปรเจกต์ jam ครั้งล่าสุดที่วิวัฒนาการไปแล้วมาใช้ — โค้ด jam มักจะรกเร็วมากภายใต้แรงกดดันเรื่องเวลา และคุณคงไม่อยากให้ hack เดือนที่แล้วแอบติดมากับเกมใหม่

6. แผนแบบชั่วโมงต่อชั่วโมงสำหรับ Jam 48 ชั่วโมง

ตารางเวลาคร่าว ๆ ช่วยไม่ให้คุณค้างที่ชั่วโมงที่ 0 หรือมารู้ตัวตอนชั่วโมงที่ 40 ว่ายังไม่มีอะไรทำงานเลย นี่ไม่ใช่กฎตายตัว — ปรับให้เข้ากับความเร็วของตัวเองได้ — แต่ checkpoint สำคัญกว่าตัวเลขชั่วโมงที่แน่นอน

Hour 0- 1 theme reveal, brainstorm, pick ONE idea Hour 1- 3 cut scope (section 4), lock the ONE core mechanic Hour 3-12 build the core mechanic: gray boxes, no art, just the verb of the game working end to end Hour 12-24 content pass: levels, waves, more interactions Hour 24-36 art and audio pass: replace gray boxes with real sprites or models, add music and SFX Hour 36-44 polish, bug fixing, playtest with a friend Hour 44-46 export the build, upload to itch.io, write the page Hour 46-48 buffer time only. submit EARLY, not at hour 47:59 Checkpoint: if hour 12 arrives and nothing is playable yet, cut scope again immediately. Do not "catch up later."

Checkpoint ชั่วโมงที่ 12 คือบรรทัดที่สำคัญที่สุดในตารางทั้งหมดนั้น "เล่นได้ภายในชั่วโมงที่ 12" หมายความว่าคนอื่นนั่งลงแล้วกดปุ่มทำ core action ของเกมคุณได้จริง — แม้จะเป็นกล่องสีเทาแทนอาร์ต แม้จะไม่มีเสียงเลยก็ตาม ถ้าถึงชั่วโมงที่ 12 แล้วยังไม่ถึงจุดนั้น ทางเลือกที่ซื่อตรงคือตัด scope เพิ่มทันทีตรงนั้นเลย ไม่ใช่ดันต่อไปโดยหวังว่าจะ "ตามทัน" ทีหลัง jam ไม่ได้ให้รางวัลกับความมองโลกในแง่ดีเรื่องชั่วโมงที่เหลือ มันให้รางวัลกับสิ่งที่รันได้จริงตอน deadline มาถึง

อีก checkpoint หนึ่งที่ควรจำไว้คือ "submit แต่เนิ่น ๆ" สร้าง package ที่ export แล้ว submit ได้จริงตัวแรกให้เสร็จก่อน deadline จริงมาก ๆ — ประมาณชั่วโมงที่ 44 ถึง 46 ในตารางข้างบน — เพราะการ export อาจล้มเหลว เว็บอัปโหลดอาจช้าลงตอนใกล้ deadline ที่ทุกคนอัปโหลดพร้อมกัน และคอมพิวเตอร์ของคุณเองก็อาจ crash ในจังหวะที่แย่ที่สุดได้ build หยาบ ๆ ที่ submit ตอนชั่วโมงที่ 45 ดีกว่า build สมบูรณ์แบบที่อัปโหลดไม่เสร็จตอนชั่วโมงที่ 48

Common mistake ใช้เวลาชั่วโมงที่ 0 ถึง 6 ไปกับการทำ shader หน้าไตเติลหรือ transition ของเมนูให้สมบูรณ์แบบ ทั้งที่ core mechanic ยังไม่ถูก prototype เลยด้วยซ้ำ ถ้าสุดท้าย core mechanic กลายเป็นไม่สนุก — ซึ่งคุณจะไม่มีทางรู้จนกว่าจะได้เล่นมันจริง ๆ — งานอื่น ๆ ที่ทำไปทั้งหมดนั้นจะไม่มีความหมายเลย และคุณเผาชั่วโมงที่เอาคืนมาไม่ได้ไปแล้ว สร้างส่วนที่สนุกก่อน หัวข้อ 7 จะอธิบายว่าทำไม และเรียงลำดับยังไง

7. Vertical Slice ก่อน: สร้างส่วนที่สนุกก่อนเมนู

vertical slice หมายถึงเส้นทางบาง ๆ หนึ่งเส้นที่ลัดผ่านทั้งเกม ทำงานได้สมบูรณ์ตั้งแต่ต้นจนจบ แทนที่จะให้ทุกระบบถูกสร้างครึ่ง ๆ กลาง ๆ พร้อมกันหมด ชื่อนี้มาจากการจินตนาการเกมของคุณเป็นเลเยอร์วางซ้อนกันตามแนวนอน (เมนู ด่าน 1 ด่าน 2 enemy AI เสียง) — vertical slice คือการตัดตรงลงมาผ่านทุกเลเยอร์พร้อมกัน แต่กว้างแค่พอที่จะพิสูจน์ว่า loop เต็มรูปแบบหนึ่งรอบทำงานได้จริง

HORIZONTAL approach (bad for a jam): main menu 20% done level 1 20% done level 2 20% done enemy AI 20% done audio 20% done result: nothing is playable, every system is a half-built stub VERTICAL SLICE approach (good for a jam): main menu 0% done level 1 100% done <- fully playable, start to finish level 2 0% done enemy AI 100% done <- only what level 1 needs audio 50% done <- only what level 1 needs result: one full loop works, you already have a game

การสร้างส่วนที่สนุก — core mechanic — ก่อนเมนู ไม่ใช่แค่ความชอบส่วนตัวเรื่องสไตล์ มีเหตุผลที่จับต้องได้สองข้อ ข้อแรก ถ้า core mechanic ไม่สนุก สิ่งอื่นที่คุณสร้างต่อจะไม่มีความหมายเลย และคุณต้องรู้เรื่องนี้ให้เร็วที่สุดในขณะที่ยังมีเวลาเปลี่ยนทิศทางได้ — เมนูหลักที่สวยงามติดอยู่กับกลไกที่น่าเบื่อ ก็ยังเป็นเกมที่น่าเบื่ออยู่ดี ข้อสอง คนให้คะแนน jam ส่วนใหญ่ใช้เวลาไม่ถึงนาทีต่อเกม — นาทีนั้นควรถูกใช้เล่น core loop ของคุณ ไม่ใช่คลิกผ่านหน้าไตเติลกับเมนู option กว่าจะไปถึง เกม jam สามารถ ถ้าเวลาหมดจริง ๆ เริ่มผู้เล่นเข้า gameplay ตรง ๆ โดยไม่มีเมนูหลักเลยก็ได้ แล้วแทบไม่เสียอะไรเลยด้วยซ้ำ

ลำดับการทำงานที่เป็นรูปธรรมซึ่งตามมาจากเรื่องนี้:

8. สิ่งที่ต้องตัดเมื่อเวลาใกล้หมด และสิ่งที่ห้ามตัดเด็ดขาด

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

เรียงลำดับคร่าว ๆ จาก "ตัดอันนี้ก่อนเลย ไม่มีใครคิดถึงมันหรอก" ไปจนถึง "ตัดอันนี้เป็นทางเลือกสุดท้ายจริง ๆ เท่านั้น":

มีสองอย่างที่ไม่มีวันอยู่ในลิสต์ที่ตัดได้ ไม่ว่าจะเหลือเวลาน้อยแค่ไหน:

Rule 1: มันต้องรันได้ build ที่ crash ตอนเปิด หรือพาผู้เล่นไปเจอจอดำตรง ๆ จะได้คะแนนเกือบศูนย์หรือศูนย์เลย ไม่ว่า idea เบื้องหลังจะดีแค่ไหนก็ตาม คนให้คะแนนให้คะแนนสิ่งที่เปิดไม่ได้ไม่ได้
Rule 2: มันต้องเข้าใจได้ภายใน 30 วินาที โดยไม่มีคุณอธิบาย คุณจะไม่ได้ยืนอยู่ข้าง ๆ ทุกคนที่เล่น submission ของคุณ ถ้าคนแปลกหน้าคิดไม่ออกว่าต้องทำอะไรภายในประมาณ 30 วินาทีหลังเปิดเกม คนให้คะแนนส่วนใหญ่จะปิด tab แล้วไปเกมถัดไปเฉย ๆ

เช็คตัวเองแบบเร็ว ๆ ก่อน submit: เอา build ให้เพื่อนลอง ต่อหน้าหรือผ่านคอล ไม่พูดอะไรเลย แล้วดูว่าเกิดอะไรขึ้นใน 30 วินาทีแรก ถ้าตอนนั้นเขากำลังทำ core action ของเกมคุณอยู่แล้ว แปลว่าเสร็จ ถ้าเขากำลังจ้องจอดำหรือถามว่า "เอ๊ะ เริ่มยังไง" ให้แก้จุดนั้นก่อนแตะอย่างอื่นเลย — มันสำคัญกว่างาน polish ที่เหลือทุกอย่างในลิสต์ของคุณ

9. ใช้ Asset ฟรีอย่างถูกกฎหมาย และให้เครดิต

ไม่มีใครคาดหวังให้คุณวาดสไปรท์ทุกชิ้นและแต่งเพลงทุกเพลงเองตั้งแต่ต้นภายใน 48 ชั่วโมง — ยกเว้นกรณีเดียว: บาง jam อย่างแทร็ก Compo ของ Ludum Dare จากหัวข้อ 3 กำหนดชัดเจนว่าต้องสร้างทุก asset เองในช่วงเวลา jam เป็นส่วนหนึ่งของความท้าทาย เช็คกฎของ jam ที่คุณเข้าร่วมก่อนเสมอ นอกเหนือจากแทร็กเดี่ยวที่เข้มงวดแบบนั้น การใช้ asset ฟรีหรือมีไลเซนส์ถือเป็นเรื่องปกติและคาดหวังได้เลย

แหล่งที่พบบ่อย: kenney.nl (คลังอาร์ตเกมขนาดใหญ่ เกือบทั้งหมดปล่อยเป็น CC0) opengameart.org (อาร์ตและเพลงที่มีไลเซนส์ปนกัน — เช็คหน้า asset แต่ละชิ้นเอง) freesound.org (เสียง effect ไลเซนส์ Creative Commons ปนกัน) และตลาด asset ของ itch.io (บาง pack ฟรี บางอันเสียเงิน ไลเซนส์ระบุไว้ในแต่ละหน้า)

ไลเซนส์บน asset แต่ละชิ้นบอกว่าคุณทำอะไรกับมันได้บ้าง สามแบบที่คุณจะเจอบ่อยที่สุด:

การให้เครดิตทำง่ายมาก: เพิ่มไฟล์ CREDITS.txt เข้าไปใน build หรือทำ section credits บนหน้า itch.io ของคุณ ระบุ asset แต่ละชิ้น ผู้สร้าง ไลเซนส์ และลิงก์

CREDITS

Music: "Chiptune Adventure" by KubaAudio (CC-BY 4.0)
  https://opengameart.org/example-music-page

SFX: Kenney "Interface Sounds" pack (CC0)
  https://kenney.nl/assets/interface-sounds

Font: "Press Start 2P" by CodeMan38 (OFL)
  https://fonts.google.com/specimen/Press+Start+2P
Common mistake ใช้ตัวละคร เพลง หรือแบรนด์ที่มีลิขสิทธิ์ซึ่งคุณไม่มีสิทธิ์ — สไปรท์มาริโอ เพลงป๊อปดัง ตัวละคร Marvel — แล้วคิดว่า "แค่ใช้ใน jam ไม่นับหรอก" มันนับจริง ๆ หลาย jam ตัดสิทธิ์ (disqualify) เกมที่ทำแบบนี้ไปเลยตรง ๆ และถ้าเกมไปต่อหลัง jam จบ (หัวข้อ 11) คุณก็ต้องรื้อของพวกนี้ออกทั้งหมดอยู่ดี ยึดกับ CC0, CC-BY ที่ให้เครดิตถูกต้อง หรือ asset ที่คุณทำเองดีกว่า

10. Submit: ชื่อเรื่อง, GIF, และ Controls

หน้า itch.io ของคุณคือความประทับใจแรกของเกม และมันเกิดขึ้นก่อนที่ใครจะกดเล่นด้วยซ้ำ สามอย่างบนหน้านั้นสำคัญกว่าที่มือใหม่คิดไว้มาก:

เลือก web build (WebGL หรือชื่อ export ที่ engine ของคุณใช้เรียก browser export) มากกว่า zip ที่ต้องดาวน์โหลด ทุกครั้งที่ engine รองรับ คนให้คะแนนมีโอกาสสูงกว่ามากที่จะคลิก "play in browser" ตรงนั้นเลย มากกว่าจะดาวน์โหลด zip แตกไฟล์ แล้วรัน .exe ที่ไม่มีลายเซ็นจากคนแปลกหน้าผ่าน antivirus warning — friction ตรงจุดนี้ทำให้คุณเสียยอดเล่นและคะแนนไปตรง ๆ

Tip ก่อน deadline ดาวน์โหลด build ที่ submit จริงของคุณเองจาก browser สด ๆ หรือคอมของเพื่อน แล้วลองใช้แบบเดียวกับที่คนแปลกหน้าจะใช้ "มันรันได้บนเครื่องฉัน" คือหนึ่งใน jam failure ที่พบบ่อยที่สุด — DLL หายไป ตั้ง build target ผิด WebGL build ที่รันได้แค่บนเครื่องตัวเองแต่รันจาก hosting จริงของ itch.io ไม่ได้ การจับปัญหานี้ได้ตอนยังเหลือเวลาอีกชั่วโมงหนึ่งเป็นสถานการณ์ที่ต่างกันมากกับการจับได้หลัง deadline ผ่านไปแล้ว

11. หลัง Jam จบ: ให้คะแนน, รับ Feedback, และตัดสินใจว่าจะขยายต่อไหม

การ submit ไม่ใช่จุดจบเป๊ะ ๆ มีสองอย่างที่ควรทำทันทีหลังจากนั้น และหนึ่งการตัดสินใจที่ควรทำเมื่อฝุ่นเริ่มจาง

อย่างแรก ให้คะแนนเกมของคนอื่น หลาย jam คาดหวังให้ทำแบบนี้ และบางงาน — ระบบจัดอันดับของ GMTK เป็นตัวอย่างที่รู้จักกันดี — ให้การมองเห็นเกมของคุณเองขึ้นอยู่กับว่าคุณให้คะแนนเกมคนอื่นไปกี่เกมด้วย นอกเหนือจากแรงจูงใจนั้น นี่คือหนึ่งในวิธีเรียนรู้ที่เร็วที่สุดจริง ๆ: คุณจะได้เห็นว่าคนอื่นอีกหลายสิบหรือหลายร้อยคนสร้างอะไรขึ้นมาภายใต้ธีมและเวลาจำกัดเดียวกันกับที่คุณมี ซึ่งทำให้เห็น idea เทคนิค และการตัดสินใจเรื่อง scope ที่คุณเองอาจไม่เคยคิดถึงได้ง่ายมาก

อย่างที่สอง อ่าน feedback ที่ได้รับ โดยไม่ต้องตั้งการ์ดป้องกันตัว comment และคะแนนที่ได้รวม ๆ กันจะบอกว่าอะไรเวิร์ก อะไรทำให้คนสับสน — ถ้าคนให้คะแนนห้าคนต่างพูดว่า "หนูไม่เข้าใจว่าโจมตียังไง" นั่นไม่ใช่ห้าคนไม่แฟร์กับคุณหรอก นั่นคือสัญญาณจริงว่ากฎ "เข้าใจได้ภายใน 30 วินาที" ในหัวข้อ 8 ของคุณยังไม่ค่อยเข้าเป้า มองหาแพทเทิร์นที่เกิดซ้ำในหลาย ๆ feedback แทนที่จะตอบสนองแรง ๆ กับ comment เดียว

อย่างที่สาม พอผ่านไปสักหนึ่งหรือสองสัปดาห์ ตัดสินใจว่าเกมนี้คุ้มค่าที่จะขยายเป็นอะไรที่ใหญ่กว่าไหม — portfolio piece ที่มีเนื้อหามากขึ้น อาร์ตดีขึ้น และ polish มากกว่าที่สุดสัปดาห์ jam จะให้ได้ ไม่ใช่ทุกเกม jam ที่สมควรได้รับสิ่งนี้ และนั่นก็โอเคเต็มที่ คุณค่าของแบบฝึกหัดนี้อยู่ที่การทำให้เสร็จ ไม่ใช่ทุกผลลัพธ์ต้องกลายเป็นของที่เก็บไว้ วิธีตัดสินใจที่สมเหตุสมผล: ถ้าคุณยังอยากเปิดมันขึ้นมาเล่นหลังจากห่างหายไปหนึ่งสัปดาห์ และ core mechanic ผ่านการทดสอบกับผู้เล่นคนอื่นด้วยดี (ไม่ใช่แค่ตัวคุณเอง) มันก็เป็นตัวเลือกที่ดีสำหรับการขยายแบบมี scope หลัง jam ถ้าทั้งสองอย่างนั้นไม่จริง ก็เก็บมันไว้เฉย ๆ เก็บส่วนที่ปรับปรุง template ระหว่างทาง (หัวข้อ 5) ไว้ใช้ต่อ แล้วเอาสิ่งที่เรียนรู้ไปใช้ใน jam ครั้งถัดไปแทน

12. ศัพท์สำคัญ

13. แบบฝึกหัด

แบบฝึกหัดที่ 1 คุณกำลังจะเข้าร่วม jam สุดสัปดาห์แบบเดี่ยว idea แรกของคุณ ทันทีที่ธีมถูกประกาศ คือ: "เกม co-op ออนไลน์ 4 คน แนว survival ที่ผู้เล่นสร้างฐาน, craft อาวุธ, สู้กับวงจรมอนสเตอร์กลางวัน/กลางคืน, ค้าขายกับเมือง NPC, และปลดล็อก skill tree" ใช้กระบวนการจากหัวข้อ 4 ตัด idea นี้อย่างน้อยสองรอบให้เหลือสิ่งที่สร้างคนเดียวได้ภายใน 48 ชั่วโมง สำหรับเวอร์ชันสุดท้าย เช็คมันกับคำถามสามข้อเรื่อง scope จาก tip box ในหัวข้อ 4
Show answer

เส้นทางการตัดที่สมเหตุสมผลแบบหนึ่ง (ของคุณไม่จำเป็นต้องตรงเป๊ะ — กระบวนการสำคัญกว่าผลลัพธ์ที่ตรงเป๊ะ):

v1 (ความฝันที่ใหญ่เกินไป): "เกม co-op ออนไลน์ 4 คน แนว survival ที่ผู้เล่นสร้างฐาน, craft อาวุธ, สู้กับวงจรมอนสเตอร์กลางวัน/กลางคืน, ค้าขายกับเมือง NPC, และปลดล็อก skill tree" ตัด: ไม่มี multiplayer/networking เลย (หัวข้อ 8 บอกว่านี่คือสิ่งแรกที่ต้องตัดเสมอ), ไม่มีเมือง NPC หรือการค้าขาย, ไม่มี skill tree

v2 (ยังใหญ่เกินไป): "Solo survival: สร้างฐานเล็ก ๆ จากผนัง 3 ชิ้น, craft อาวุธ 2 แบบ, รอดจากวงจรกลางวัน/กลางคืนที่มอนสเตอร์โจมตีเฉพาะตอนกลางคืน" ตัด: ไม่มีระบบสร้างฐาน (การสร้างฐานภายใต้แรงกดดันเรื่องเวลาหมายถึง logic การวาง, การ snap, และ UI ของตัวเอง — เยอะเกินไปสำหรับหนึ่งสุดสัปดาห์), ไม่มีการ craft, เหลือแค่ไอเดียเรื่องจังหวะมอนสเตอร์กลางวัน/กลางคืน

v3 (สร้างได้ในหนึ่งสุดสัปดาห์): "อารีน่าคงที่หนึ่งอัน มอนสเตอร์ spawn เฉพาะช่วง 'กลางคืน' ที่วนซ้ำ แสดงด้วยหน้าจอที่มืดลง ผู้เล่นมีอาวุธหนึ่งชิ้นและต้องรอดในแต่ละช่วงกลางคืน โดยมีช่วง 'กลางวัน' ที่ปลอดภัยสั้น ๆ ให้ฟื้นตัวระหว่างเวฟ คะแนน = จำนวนคืนที่รอดชีวิต"

เช็ค scope บน v3: (1) วิธีแพ้ — มี คือโดนมอนสเตอร์จับได้ระหว่างช่วงกลางคืน (2) วิธีชนะหรือคะแนน — มี คือจำนวนคืนที่รอด ซึ่งคนให้คะแนนอ่านเข้าใจทันที (3) input แกนกลางที่ต้องเรียนรู้หนึ่งอย่าง — มี โดยพื้นฐานคือแค่โจมตี/เล็ง ผ่านทั้งสามข้อ v3 จึงเป็น scope ที่สร้างได้จริง

แบบฝึกหัดที่ 2 หัวข้อ 6 ให้แผนแบบชั่วโมงต่อชั่วโมงสำหรับ jam 48 ชั่วโมง โดยมี checkpoint สำคัญคือ "เล่นได้ภายในชั่วโมงที่ 12" (หนึ่งในสี่ของเวลาทั้งหมด) แต่คุณเข้าร่วมแทร็ก Jam 72 ชั่วโมงของ Ludum Dare แทน (หัวข้อ 3) ปรับสัดส่วนแผนจากหัวข้อ 6 ให้เป็น 72 ชั่วโมง โดยรักษาสัดส่วนเวลาทั้งหมดของแต่ละเฟสไว้เท่าเดิม แล้วบอก checkpoint ที่เล่นได้ใหม่
Show answer

72 ชั่วโมงคือ 1.5 เท่าของ 48 ชั่วโมง ดังนั้นคูณขอบเขตชั่วโมงทุกอันจากหัวข้อ 6 ด้วย 1.5 จะได้:

Hour 0- 1.5 theme reveal, brainstorm, pick ONE idea Hour 1.5- 4.5 cut scope, lock the ONE core mechanic Hour 4.5-18 build the core mechanic, playable end to end Hour 18-36 content pass: levels, waves, more interactions Hour 36-54 art and audio pass Hour 54-66 polish, bug fixing, playtest with a friend Hour 66-69 export the build, upload, write the page Hour 69-72 buffer time only. submit EARLY New checkpoint: playable by hour 18 (still a quarter of the total 72 hours, matching the 12-of-48 fraction from section 6).

ไอเดียหลักที่แบบฝึกหัดนี้ทดสอบคือ checkpoint เป็นสัดส่วนของเวลา jam ทั้งหมด (หนึ่งในสี่) ไม่ใช่จำนวนชั่วโมงตายตัว jam ที่ยาวกว่าให้ชั่วโมงต่อเฟสมากกว่า แต่สัดส่วนเดิมของ "หา core mechanic ให้เจอ" เทียบกับ "สร้างเนื้อหา" เทียบกับ "polish" ยังคงใช้ได้เหมือนเดิม

แบบฝึกหัดที่ 3 AudioManager ต่อไปนี้ ที่ copy เข้าไปใน template jam แบบรีบ ๆ ขาดบางอย่างไปจากเวอร์ชันในหัวข้อ 5 ระหว่าง jam การ restart ด่านหลัง game-over จะโหลด scene ทั้งหมดใหม่ ซึ่งมีสำเนา Bootstrap object ของตัวเองอยู่ด้วย ผู้เล่นรายงานว่าได้ยินเพลงซ้อนกันสองเพลงหลัง restart ไปสักสองสามครั้ง หาบั๊กแล้วแก้ไข
using UnityEngine;

public class AudioManager : MonoBehaviour
{
    public static AudioManager Instance { get; private set; }

    public AudioSource musicSource;

    void Awake()
    {
        Instance = this;
        DontDestroyOnLoad(gameObject);
    }

    public void PlayMusic(AudioClip clip)
    {
        musicSource.clip = clip;
        musicSource.loop = true;
        musicSource.Play();
    }
}
Show answer

บั๊กคือ guard ป้องกัน instance ซ้ำที่หายไปจากหัวข้อ 5 Awake ที่นี่ตั้ง Instance = this และเรียก DontDestroyOnLoad(gameObject) แบบไม่มีเงื่อนไข โดยไม่เช็คเลยว่า Instance มีอยู่แล้วหรือเปล่า ทุกครั้งที่ scene ของด่าน reload (ซึ่งมีสำเนา Bootstrap ของตัวเอง) AudioManager ตัวใหม่เอี่ยมจะรัน Awake เขียนทับ Instance ให้ชี้ไปที่ตัวเอง แล้วก็เรียก DontDestroyOnLoad กับตัวเองด้วย — แต่ AudioManager ตัวก่อนหน้า จากการโหลด scene ครั้งก่อนนั้น ไม่เคยถูกทำลายเลย มันยัง alive อยู่ ยังมี musicSource ของตัวเองเล่นวนอยู่ และไม่มีอะไรหยุดมันเลย พอ restart ไปสักสองสามครั้ง คุณจะมี AudioManager สามสี่ตัวเล่นเพลงพร้อมกันเงียบ ๆ อยู่

using UnityEngine;

public class AudioManager : MonoBehaviour
{
    public static AudioManager Instance { get; private set; }

    public AudioSource musicSource;

    void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
        DontDestroyOnLoad(gameObject);
    }

    public void PlayMusic(AudioClip clip)
    {
        musicSource.clip = clip;
        musicSource.loop = true;
        musicSource.Play();
    }
}

พอใส่ guard กลับเข้าไป AudioManager ตัวใหม่ทุกตัวจะเช็คก่อนว่ามีตัวอื่นเป็น Instance ที่ยัง alive อยู่แล้วหรือเปล่า ก่อนจะทำอะไรต่อ ถ้ามี ตัวที่ซ้ำเข้ามาใหม่จะทำลายตัวเองทันทีใน Awake ก่อนที่จะมีโอกาสแตะ DontDestroyOnLoad หรือเริ่มเล่นอะไรเลยด้วยซ้ำ ไม่ว่า scene ของด่านจะ reload กี่ครั้งก็ตาม จะมี AudioManager ที่ alive อยู่แค่ตัวเดียวเสมอ

บทนี้ครอบคลุมเรื่อง game jam แบบครบวงจร: jam คืออะไรจริง ๆ jam หลัก ๆ ที่เข้าร่วมได้ในปีนี้ ทำไม deadline เองถึงเป็นสิ่งที่สอนอะไรคุณได้ วิธีตัด idea ให้เหลือ scope ขนาดสุดสัปดาห์จริง ๆ แผนแบบชั่วโมงต่อชั่วโมงพร้อม checkpoint เล่นได้ที่หนึ่งในสี่ของเวลาแบบเข้มงวด การสร้างส่วนที่สนุกก่อนเมนู สิ่งที่ต้องตัดและกฎสองข้อที่ห้ามตัดเด็ดขาด การใช้ asset ฟรีโดยไม่ผิดไลเซนส์ของใคร การเตรียม template เพื่อให้ชั่วโมง jam ไปลงกับเกมจริงของคุณ และหน้า submission ต้องมีอะไรบ้างถึงจะได้ 30 วินาทีของคนแปลกหน้ามา ครั้งต่อไปที่คุณเห็นการประกาศธีม คุณจะรู้เส้นทางทั้งหมดจากศูนย์ไปจนถึงเกมที่เสร็จและ submit แล้วอยู่แล้ว

← กลับไปหน้ารวมบท