שפת תכנות ומשחק ב7 ימים
בשבוע השלישי של דצמבר, במשך שבעה ימים, בניתי שפת תכנות ואז בניתי איתה משחק.
הכל התחיל לפני כחודש כשנכנסתי להאקר ניוז כפי שאני עושה מדי פעם ונתקלתי בהכרזה מעניינת על גיים ג'אם חדש בשם Langjam Gamejam.
גיים ג'אמים הם אירועים בהם המשתתפים בונים משחק, בדרך כלל בפרק זמן קצוב, בנושא ספציפי, ובמגבלות מסויימות. כל אירוע הוא שונה לפי הטעם של המארגנים - יש כאלה שמתרחשים במקום פיזי ספציפי ויש כאלה שפתוחים לכל דרך האינטרנט, יש כאלה שלוקחים חודש ויש כאלה שלוקחים שעה, יש כאלה שדורשים קוד פתוח ואחרים שמגבילים שימוש בפלטפורמות או מוטיבים, ועוד ועוד.
המארגנים של Langjam Gamejam הציעו למשתתפים אתגר מיוחד ושונה משאר הגיים ג'מים אני מכיר - עליכם לא רק לבנות משחק, אלא גם לעצב ולממש את שפת תכנות בה תכתבו את המשחק, וכל זאת ב7 ימים.
איזה יופי! הגיים ג'אם הזה משלב שני דברים שאני מאוד אוהב (שפות תכנות ומשחקים) ומגדיר לי פרק זמן תחום בו אני יכול לאתגר את עצמי.
לא באמת הצטרפתי לאירוע עצמו, כי אני כבר לא ממש מצטרף לאירועים של אנשים מחו"ל בעולם, אבל כן החלטתי לאמץ את הרעיון ולעצב שפת תכנות ולבנות איתה משחק במקביל לתאריכים של האירוע (פחות או יותר). התוצאה היא עין 👁️🗨️.
השפה
עין היא שפת תכנות פרוצדורלית פשוטה שלוקחת השראה בעיקר מג'אווה סקריפט (וקצת מראסט). יש בה ממש מעט פיצ'רים אבל בתוספת מעטפת ומערכת ריצה מסתבר שהם דיי מספיקים כדי ליצור משחק פשוט.
יש בה 4 טיפוסים פשוטים: בוליאנים, מחרוזות, מספרים שלמים ומספרים עם נקודה צפה, ועוד שני מבני נתונים: מערכים ומילונים/אובייקטים. יש בה משתנים, פונקציות (first-class), תנאים ולולאות, והיכולת לפצל קוד למספר קבצים. זהו.
תוכנית בסיסית בעין נראית כך:
include "../stdlib/stdlib.ayin"
let setup = fn() {
{
.color: {
.r: 0,
.g: 0,
.b: 100,
},
}
}
let update = fn(state, input) {
let x =
if input.gamepad1.dpad.up {
1
} else {
0
} + if input.gamepad1.dpad.down {
-1
} else {
0
}
let new_blue = state.color.b + x
state.color.b = min(255, max(0, new_blue))
}
let draw = fn(state) {
frame_clear(state.color.r, state.color.g, state.color.b)
}
עין היא שפה יחסית מינימליסטית (מן הסתם, 7 ימים). אבל בכנות אני לא מרגיש שחסר בה יותר מדי פיצ'רים למטרה (המימוש כנראה לוקה קצת בחסר).
המימוש
המפרש של עין כתוב בשפת Rust - שפה יחסית מודרנית עם הרבה מאוד פיצ'רים כשהמשמעותי (ואפילו מהפכני) מביניהם הוא ניהול זכרון "אוטומטי" ללא מערכת ריצה או מנגנון איסוף זבל, כל עוד מצליחים לכתוב קוד שמקיים את התנאים שמציבה השפה.
בחרתי בראסט כי מצד אחד עבדתי איתה בעבר אז הכרתי אותה בצורה סבירה, מצד שני יש לה פיצ'ר בשם טיפוסים אלגבריים שבעיניי מאוד שימושי כשמממשים שפות תכנות, וגם מבני נתונים כמו מפות ומערכים בספרייה הסטנדרטית, ומצד שלישי כי הייתה לי פנטזיה כלשהי שאצליח להריץ בסופו של דבר את המשחק על קונסולת משחק ניידת (דבר שבסוף לא קרה) ולשם כך רציתי תאימות טובה עם ARM ולהקטין את צריכת הזכרון האפשרית.
מול ראסט התמודדו גם זיג ואודין, שבהן אין לי ניסיון קודם, הסקל שאין לה תאימות טובה עם ARM וצורכת יותר זכרון בשימוש רגיל מהשפות האחרות ברשימה, ו-סי שלה אין טיפוסים אלגבריים או מבני נתונים נפוצים בספרייה הסטנדרטית.
יש לי רגשות מעורבים לגבי השימוש בראסט בפרויקט. בעוד שאני דיי שמח מהתוצר, נתקלתי בלא מעט קשיים הקשורים גם בדרכים לייצג את מבני הנתונים שלי וגם בספריות דיי מסובכות לשימוש והבנה בלי דוקומנטציה מספיק טובה וברורה. בדיעבד בסיכוי גבוה שכנראה הייתי הולך על הסקל במקום, שכנראה הייתה מקצרת לי את העבודה בלפחות 50%, או בסי איתה לפחות לא הייתי צריך לשבור את הראש יותר מדי.
שני חלקים ספציפית שהיו יותר מאתגרים מאחרים היו לכתוב את הפרסר שמפרש את הטקסט והופך אותו למבני נתונים (אותו כתבתי ביד בסוף), ולמצוא דרך לייצג בראסט סביבה המכילה דאטה מקונן (עם מילונים/אובייקטים) שהם גם mutable (בסי הייתי פשוט משתמש במצביעים).
החלפת קוד "חמה"
כשחשבתי על המימוש של שפת התכנות שלי הרגשתי שיש לי פה הזדמנות ליצור משהו שהוא קצת מעבר. אחד הדברים החשובים בבניית משחקים הוא לקצר את ה"פידבק לופ" - הזמן שלוקח מהרגע שעושים שינוי עד לרגע שבו הוא מופיע במסך.
לפני כמה שנים ראיתי סרטון הדגמה של מישהו מ-Tomorrow Corporation, החברה מאחורי המשחקים World of Goo ו- Human Resource Machine, בו הוא הציג את סביבת הפיתוח שלהם. מסתבר שיש להם שפה משלהם עם מימוש משלהם ועורך בו ניתן להריץ את המשחק ותוך כדי לשנות את הקוד והאסטים ולראות את השינוי מיד ללא הצורך להתחיל את המשחק מהתחלה (בדמו עוד דברים מאוד מאוד מגניבים, אבל זה העיקרי לעניין).
הדמו הזה נשאר איתי מאז, ובהזדמנות הזו חשבתי שאם כבר אני בונה שפת תכנות לפיתוח משחק, זה בהחלט פיצ'ר שהייתי רוצה!
לכן המימוש של עין יכול ✨להסתכל✨ על קבצי קוד המקור ולעדכן את הקוד כשיש שינוי בקבצים תוך כדי שהמשחק רץ בלי לשנות את מצב המשחק! זה פיצ'ר שאני מאוד גאה בו והאמת שהוא לא היה קשה למימוש בזכות הדומיין הספציפי של פיתוח משחקים והבחירות שעשיתי.
הדגמה של Hot code reloading
למרות זאת הפיצ'ר הזה הצריך לא מעט מחשבה ומחקר בשבוע שלפני הג'אם. בכלל, הג'אם הזה עבד טוב בגלל שבשבוע שלפני יכלתי לחשוב, לחקור ולהחליט מה אני רוצה לעשות, וכך במהלך הג'אם יכלתי להקדיש את הזמן למימוש.
המשחק
המשחק עצמו ממש לא מרשים ומאוד פשוט, אבל יש בו טקסט, ריבועים, צבעים, טיימרים, ושליטה על ידי שלט משחק או חיצי המקלדת.
אם תרצו, תוכלו לשחק בו
בעמוד המשחק!
סיכום
לסיכום, מאוד נהנתי מהאתגר ואני מרוצה מהתוצאה אליה הצלחתי להגיע, אם כי היו לי תוכנית קצת יותר גרנדיוזיות, אבל בשביל סדר גודל של 40 שעות עבודה בכמה ערבים וסופ"ש (וגם קצת בטלפון באוטובוס 😅), אני שמח ממה שיצא.
אם אנסה את האתגר הזה שוב, כנראה אבחר בשפת מימוש שונה, ואחרי שעשיתי שפה פרוצדורלית פשוטה, סביר שאבחר בפרויקט מעט שונה, כמו שפה יותר forth-ית או lisp-ית, או מימוש שונה כמו קומפיילר או מכונה וירטואלית. כך או כך, זה בטוח יהיה אתגר מעניין!
רוצים להגיב? בדקו מהי שאלת הסינון בעמוד הראשי.