x
בניית אתרים בחינם
הפוך לדף הבית
הוסף למועדפים
שלח לחבר
 
                     הכל על הכל©       

        

 
    דף הבית
    סרגל כלים לאתר
    מה זה איקרים?
    פורום איקרים
    איך נרשמים ?
    מדריך למתחיל
    פרטי לצבא
    פלאים באיקרים
    מ חקרים ומה הם עושים
    איפה כדאי לפתוח ערים ?
    איך לעלות שמחה ?
    איך נראה הערים מבחוץ בכל שלב ?
    איך נראה הערים מבפנים בכל שלב
    בריתות טובות
    Game Maker
    פורום בניית משחקים
    איך ליצור משחק פונג
    איך ליצור משחק פקמן
    המשחקים שלנו
    איך ליצור משחק חייזרים
    איך ליצור משחק טטריס
    תחרות משחקים !!!
    צור קשר
    דף סקרים
    איך עושים סרגל כלים
    סרטים להורדה

תכנות לילדים באמצעות Game Maker/טטריס/צעד 1: צורה

מתוך ויקיספר, אוסף ספרי הלימוד והמדריכים החופשי.



 הצורות

בטטריס יש מעט מאוד דברים. הדבר העיקרי שיש במשחק זה צורות שנופלות. יש שבע צורות כאלה:

תמונה:tetris_O_shape.png תמונה:tetris_I_shape.png תמונה:tetris_T_shape.png תמונה:tetris_L_shape.png תמונה:tetris_J_shape.png תמונה:tetris_Z_shape.png תמונה:tetris_S_shape.png


עכשיו תורך:

לשמור את הצורות אצלך במחשב, וליצור להן דמויות במשחק. (עדיין לא ליצור אובייקטים!) כדאי לתת לדמויות שמות משמעותיים; נהוג להשתמש בשמות האותיות באנגלית שצורתן דומה לצורות של טטריס: O, I, T, L, J, Z, S.



מה הצורות האלה עושות?

  • נופלות למטה בקפיצות.
  • זזות כשלוחצים על החצים ימינה ושמאלה.
  • מסתובבות כשלוחצים על חץ למעלה.
  • נערמות אחת על השניה אחרי שהן נופלות.
  • נעלמות אם יש בערימה שורה שלמה.

אז זו רשימת העבודה שלנו כרגע.

במקום ליצור מיד 7 אובייקטים כדי שלכל צורה יהיה אובייקט, נתחיל מאובייקט אחד לצורה אחת. ככה לא נצטרך לשנות ולתקן 7 אובייקטים כל פעם, אלא רק אחד. אחרי שנשלים את הצורה הראשונה, יהיה ברור איך לתכנת את האחרות. לכן נוסיף לסוף רשימת העבודה עוד סעיף:

  • לתכנת את שאר הצורות.

מאיזו צורה נתחיל?
בדרך כלל עדיף להתחיל במה שנראה הכי פשוט, כדי שקודם כל נצליח ליצור משהו פשוט שעובד; אחר כך נוכל להוסיף לו דברים יותר מורכבים. הצורה הכי פשוטה מבין שבע הצורות היא הריבוע, כי הוא לא מסתובב. לכן רשימת העבודה המעודכנת היא:

  • אובייקט לצורת הריבוע.
  • נפילה.
  • תנועה ימינה ושמאלה כשלוחצים על החצים.
  • להערם בסוף הנפילה.
  • להעלים שורות שלמות.
  • לתכנת את שאר הצורות.
  • לסובב את הצורות כשלוחצים על חץ למעלה.

 אובייקט צורה נופלת

קודם כל ניצור אובייקט בשם shape (שֵייפּ) - צורה. ניתן לאובייקט הצורה את דמות הריבוע. ניצור חדר ונשים בו את אובייקט הצורה.


תמונה:gmaker_test_save.png כדאי לוודא שרואים את צורת ההריבוע.



עכשיו צריך לגרום לצורה ליפול. עד עכשיו גרמנו לאובייקטים לזוז באמצעות קביעת המהירות שלהם. אבל בטטריס הצורות לא זזות במהירות קבועה, אלא יורדות מדי פעם בקפיצה. לכן הפעם נטפל בתנועה של הצורות בצורה שונה: נשנה את הקורדינטות של הצורה בכל פעם שהיא צריכה לזוז. איך נדע מתי היא צריכה לזוז? נכוון שעון מעורר:

  1. תמונה:gmaker_create.png נוסיף לאובייקט הצורה ארוע יצירה.
  2. תמונה:gmaker_clock_button.png בארוע זה נשים פעולת כיוון שעון מעורר. נכוון את השעון ל-20 צעדים.
  3. תמונה:gmaker_alarm.png נוסיף ארוע צלצול שעון.
  4. תמונה:gmaker_set_var.png בארוע הזה נשנה את הערך של המשתנה y כדי להזיז את הצורה למטה: נוסיף לו 16 (לסמן Relative).
  5. תמונה:gmaker_clock_button.png נוסיף גם לארוע הזה פעולת כיוון שעון מעורר ושוב נכוון את אותו השעון ל-20 צעדים. ככה הצורה תרד שוב ושוב כל 20 צעדים.

תמונה:gmaker_test_save.png עכשיו הצורה אמורה ליפול לאיטה, כל פעם בקפיצה של 16 פיקסלים.



ברוב הגירסאות של טטריס, לחיצה על חץ למטה גורמת לצורה ליפול במהירות גדולה יותר. אצלנו השעון המעורר קובע את מהירות הנפילה, לכן כשלוחצים על חץ למטה צריך לכוון את השעון למספר צעדים קטן:

  1. תמונה:gmaker_key.png להוסיף ארוע לחיצה על חץ למטה (Down).
  2. תמונה:gmaker_clock_button.png בארוע הזה לכוון את השעון המעורר ל-1 צעדים.

תמונה:gmaker_test_save.png נסו ללחוץ על חץ למטה.



 תנועה לצדדים

גם את התנועה לצדדים נעשה באמצעות שינוי הקורדינטות:

  1. תמונה:gmaker_key.png נוסיף ארוע לחיצה על חץ ימינה (Right).
  2. תמונה:gmaker_set_var.png בארוע הזה נשנה את המשתנה x כדי להזיז את הצורה ימינה: נוסיף לו 16 (לסמן Relative). ככה הצורה תזוז ימינה 16 פיקסלים.
  3. נעשה אותו הדבר לתנועה שמאלה (Left), רק שהפעם נוסיף ל-x את הערך -16 כדי להזיז את הצורה שמאלה.

תמונה:gmaker_test_save.png נסו להזיז את הצורה ימינה ושמאלה בעזרת החצים.



הצורה זזה כשלוחצים על החצים, אבל היא זזה מהר מדי ואז קשה לכוון איפה היא תהיה בדיוק. בטטריס חשובה גם תנועה מהירה (בעיקר כשהמשחק מתקדם ונהיה מהיר) אבל גם הדיוק חשוב, כדי שיהיה אפשר להפיל את הצורות בדיוק למקום הנכון. הדרך הנהוגה להשיג גם דיוק וגם מהירות היא שבהתחלת הלחיצה הצורה תזוז רק פעם אחת, ואם המשתמש ממשיך ללחוץ אז הצורה זזה מהר. כלומר: צריך להפריד בין התזוזה הראשונה לתזוזות הבאות, ככה שתהיה השהיה אחרי התזוזה הראשונה.

 הפרדת התזוזה הראשונה

גם במשחק חייזרים היינו צריכים להפריד בין דברים שקרו מהר מדי: שם הפרדנו בין היריות בעזרת משתנה מצב. גם הפעם נשתמש במשתנה מצב כדי להפריד בין התזוזות. אבל הפעם למשתנה שלנו יש 3 מצבים:

  1. לפני התזוזה הראשונה - אפשר לזוז.
  2. אחרי התזוזה הראשונה - לא לזוז עד שנגמרת ההשהיה.
  3. אחרי ההשהיה - שוב אפשר לזוז.

נקרא למשתנה המצב שלנו move_state. הוא יוכל לקבל אחד משלושה ערכים:

  • לפני התזוזה הראשונה הערך שלו יהיה 0.
  • בזמן ההשהיה שאחרי התזוזה הראשונה הערך יהיה 1.
  • אחרי שנגמרה ההשהיה הערך יהיה 2.

קודם כל נאתחל את המשתנה: נוסיף לארוע היצירה את הפעולה לקביעת ערך של משתנה תמונה:gmaker_set_var.png ושם נקבע את הערך של המשתנה move_state ל-0.

גם כשמפסיקים ללחוץ על החץ, צריך להחזיר את המשתנה move_state לערך 0, כדי שהוא יהיה מאותחל בפעם הבאה שהמשתמש ילחץ על חץ. לכן נוסיף שני ארועי שחרור מקש תמונה:gmaker_key_up.png - אחד עבור חץ ימינה (Right) ואחד עבור חץ שמאלה (Left). בארועים האלה נשים את הפעולה לקביעת ערך של משתנה תמונה:gmaker_set_var.png ונקבע את הערך של המשתנה move_state ל-0.

עכשיו נעבור לתזוזה עצמה: ארוע לחיצה על חץ תמונה:gmaker_key.png. מה צריך לעשות בארוע הזה?

  • במצב 2 (תזוזה מהירה) צריך פשוט לזוז. כלומר: לשנות את הערך של x.
  • במצב 1 (המתנה) צריך לחכות בלי לזוז. כלומר: לא לעשות כלום.
  • במצב 0 (תזוזה ראשונה) צריך לזוז, לעבור למצב 1, ולכוון שעון מעורר שיצלצל בסוף ההמתנה.

נתחיל במצב 1, כי אז צריך לא לעשות כלום. לכן נשים בתחילת רשימת הפעולות את הפעולה לבדיקת ערך של משתנה תמונה:gmaker_if_var.png ובה נבדוק אם move_state שווה ל-1. ככה הפעולה הבאה תתבצע רק במצב 1.

אבל הפעולה שצריך לעשות במצב 1 היא לא לעשות כלום! איך לא עושים כלום?

 הפסקת ביצוע פעולות

בלשונית control יש פעולה ליציאה מהארוע: תמונה:gmaker_exit.png. הפעולה הזאת מפסיקה את ביצוע הפעולות של הארוע. ככה כל הפעולות שמופיעות אחריה ברשימה לא יבוצעו. את הפעולה הזו נשים אחרי פעולת הבדיקה, וככה אם המצב הוא 1 אז לא יבוצעו פעולות נוספות.

עכשיו נוכל לטפל במצבים האחרים. גם במצב 0 וגם במצב 2 צריך להזיז את הצורה, לכן נוכל לשים עכשיו את הפעולה לשינוי המשתנה x תמונה:gmaker_set_var.png. היא תתבצע רק אם פעולת הבדיקה נתנה תוצאה שלילית, כלומר: במצבים 0 ו-2.

במצב 2, זה כל מה שצריך לעשות. לכן אחרי הפעולה שמשנה את x, נשים שוב את הפעולה לבדיקת ערך של משתנה תמונה:gmaker_if_var.png והפעם נבדוק אם move_state שווה ל-2. אם כן, לא צריך לבצע פעולות נוספות. לכן אחרי פעולת הבדיקה נשים את הפעולה ליציאה מהארוע תמונה:gmaker_exit.png.

כל הפעולות הבאות שנשים יבוצעו רק אם שתי פעולות הבדיקה נתנו תוצאה שלילית, כלומר: אם move_state שווה 0.

מה נשאר לעשות במקרה הזה?

  1. תמונה:gmaker_set_var.png לשנות את הערך של המשתנה move_state ל-1.
  2. תמונה:gmaker_clock_button.png לכוון שעון מעורר חדש ל-10 צעדים.

הנה רשימת הפעולות השלמה בארוע הלחיצה על חץ ימינה:

(הפעולות בארוע לחיצה על חץ שמאלה זהות, מלבד הפעולה לשינוי x.)

נשאר עוד דבר אחד: להוסיף ארוע צלצול שעון מעורר תמונה:gmaker_alarm.png עבור השעון החדש, ובו לשנות את הערך של move_state ל-2 תמונה:gmaker_set_var.png כדי לסמן שהסתיימה ההמתנה שאחרי התזוזה הראשונה.



תמונה:gmaker_test_save.png כשלוחצים על חץ ימינה או שמאלה צריכה להיות המתנה בין התזוזה הראשונה לתזוזות הבאות.
 

גבול

את האזור שבו הצורות יכולות לזוז נתחום בעזרת אובייקט גבול: אובייקט יציב (Solid) בשם border עם דמות של ריבוע בגודל 16 פיקסלים, לא שקופה (Transparent). נציב אובייקטים כאלה בחדר ככה שייווצר אזור מתאים למשחק, כמו שרואים בתמונה למטה. בדרך כלל רוחב אזור המשחק בטטריס הוא 10 או 12 משבצות, והגובה 18 או 20 משבצות מהתחתית של הצורה.

תמונה:gmaker_tetris_border.png


תמונה:gmaker_test_save.png הגבול לא עוצר את הצורה, כי עוד לא תכנתנו אותם להכיר אחד את השני.



בעבר השתמשנו בארוע התנגשות כדי שאובייקטים לא יעברו את הגבול. הפעם נעשה את זה בדרך אחרת: כמו שהזזנו את הצורה בעצמנו, ככה גם נעצור אותה בעצמנו, בלי להשתמש בארוע ההתנגשות. יש לזה שני יתרונות:

  1. נשתמש בפעולה לבדיקת התנגשות תמונה:gmaker_if_collide_button.png שמאפשרת לבדוק התנגשות בכל האובייקטים ולא רק בגבול. ככה נוכל לעצור את הצורה בין אם היא מגיעה לגבול ובין אם היא מתנגשת בצורות שנפלו קודם - נוכל לטפל בשני המקרים האלה בבת אחת.
  2. נצטרך לתכנת בנפרד את העצירה של תנועה למטה, ובנפרד את העצירה של תנועה הצידה. לכאורה זה יותר עבודה, אבל למעשה ההתנהגות בשני המקרים שונה: בתנועה הצידה צריך רק לעצור את הצורה, ואילו בתנועה למטה צריך להפוך את הצורה לחלק מערימת הצורות שכבר נחתו, ואז להפיל מלמעלה צורה חדשה.

כמובן שאפשר לעשות את הדברים האלה גם בארוע התנגשות אם רוצים, אבל במקרה של טטריס נוח יותר לעצור את הצורה בעצמנו.

בשביל לעצור את הצורה בגבול כשלוחצים על החצים צריך ללכת לארועי החצים ימינה ושמאלה ולהוסיף פעולת בדיקה לפני הפעולה שבה משנים את x. ככה הצורה תזוז רק אם הדרך פנויה.

פעולת הבדיקה שבה נשתמש היא הפעולה לבדיקת מקום פנוי תמונה:gmaker_if_free_button.png שבה השתמשנו גם בפקמן. הפעולה הזו היא ההפך של הפעולה לבדיקת התנגשות: היא בודקת אם מקום מסוים הוא פנוי, ככה שהפעולה הבאה תתבצע רק אם אין בו התנגשות.

בחלון אפשרויות הפעולה צריך לכתוב 16 בשדה x (או -16 אם התנועה היא שמאלה) ו-0 בשדה y. ככה הפעולה תבדוק אם המקום שמימין (או משמאל) פנוי. (כמובן שצריך לסמן Relative.)

את השדה objects אפשר להשאיר על Solid only.


תמונה:gmaker_test_save.png נסו להזיז את הצורה ימינה ושמאלה עד הגבול.



עכשיו צריך לטפל במקרה שהצורה נוחתת למטה על הגבול (או על הערימה של הצורות שנחתו קודם).

 נחיתה

כדי לגלות מתי הצורה נוחתת, נלך לארוע צלצול השעון המעורר שבו מפילים את הצורה. לפני הפעולה לשינוי y (שגורמת לצורה ליפול) נכניס פעולה לבדיקת מקום פנוי תמונה:gmaker_if_free_button.png ובה נבדוק את המיקום שמתחת למיקום הנוכחי, כלומר: Relative, בשדה x הערך 0, ובשדה y הערך 16. ככה הצורה תיפול רק אם היא לא מגיעה לגבול (או לאובייקטים יציבים אחרים, כמו הצורות שנחתו לפניה).


תמונה:gmaker_test_save.png הביאו את הצורה עד למטה ותראו מה קורה.



אחרי שהצורה נוחתת, צריך להפוך אותה לחלק מהערימה של צורות שכבר נחתו, ולהפיל צורה חדשה מלמעלה. הצורות שנחתו הן אובייקט אחר מאובייקט הצורה: הן לא נופלות ולא זזות ימינה ושמאלה כשלוחצים על החצים. לכן נצטרך אובייקט חדש בשבילן. בואו נדחה את זה כרגע, ונעשה רק את החלק השני: להפיל צורה חדשה מלמעלה.

איך נפיל צורה מלמעלה? אפשר ליצור אובייקט צורה חדש שייפול. אבל מאחר שממילא כבר לא צריך את הצורה שנחתה והפכה לערימה, אפשר פשוט להחזיר אותה לנקודת ההתחלה תמונה:gmaker_jump_to_start.png וככה להשתמש בה שוב. ממילא יש לנו כרגע רק צורה אחת, אז בינתיים אפשר להשתמש בה שוב. כשנתכנת צורות אחרות נצטרך לדאוג לשנות את הצורה שנופלת.

את פעולת החזרה לנקודת ההתחלה תמונה:gmaker_jump_to_start.png צריך לעשות במקרה שהפעולה לבדיקת מקום פנוי נתנה תשובה שלילית. כלומר: צריך לשים פעולת בדיקת ההפך ("אחרת") תמונה:gmaker_else.png שאחריה נחזיר את הצורה לנקודת ההתחלה. רשימת הפעולות של ארוע צלצול השעון צריכה להראות ככה:

ההערה האחרונה ("צריך: להפוך לערימה") מסמנת שיש שם משהו שעוד צריך לעשות: ליצור במקום הצורה אובייקט (או אובייקטים) שיהיו חלק מהערימה של הצורות שכבר נחתו. כשנגיע לתכנת את הערימה, יהיה אפשר להוסיף מתחתיה את הפעולות שמתאימות, ולמחוק מההערה את המלה "צריך".


תמונה:gmaker_test_save.png כדאי לוודא שאחרי שהצורה נוחתת היא חוזרת ליפול מהתחלה.



 הנחתה

בהרבה גירסאות של טטריס אפשר "להנחית" צורה למטה אם לוחצים על מקש רווח. בשביל לתכנת הנחתה נוסיף ארוע הקשה על מקש רווח (Space) תמונה:gmaker_key_down.png. בארוע הזה נרצה להוריד את הצורה עד לגבול שלמטה, או עד לראש הערימה של הצורות שנחתו לפניה. במלים אחרות: להוריד את הצורה עד שהיא תגיע לאובייקט יציב.

בלשונית move יש פעולה שעושה בדיוק את זה: פעולת התזוזה עד לנקודת מגע תמונה:gmaker_contact_button.png. הפעולה הזאת מזיזה את האובייקט עד שהוא מגיע לנקודה בה הוא נתקל באובייקט אחר.

בחלון אפשרויות הפעולה יש שלושה שדות:

  • direction הוא הכיוון שאליו מזיזים את האובייקט. אנחנו רוצים להזיז אותו למטה, לכן נכתוב שם 270.
  • maximum אומר עד כמה רחוק להזיז את האובייקט, במקרה שלא נתקלים בשום אובייקט בדרך. -1 מסמן שאין מגבלה על המרחק של התזוזה, לכן נשאיר את השדה הזה בערך -1.
  • against אומר מול איזה מין אובייקטים לבדוק התנגשות - כל האובייקטים או רק היציבים. אנחנו יכולים להסתפק באובייקטים היציבים, לכן נשאיר את השדה הזה על solid objects.

אחרי שהורדנו את הצורה למטה, צריך לגרום לה לנחות - להפוך לחלק מהערימה ולעבור חזרה למעלה כדי ליפול מהתחלה. את זה עשינה כבר בארוע השעון המעורר, לכן במקום לכתוב את זה שוב פשוט נוסיף את הפעולה לכיוון השעון המעורר תמונה:gmaker_clock_button.png ונכוון את השעון לצלצל עוד 1 צעדים.


תמונה:gmaker_test_save.png לחיצה על רווח אמורה להנחית את הצורה.



 עדכון רשימת העבודה

בואו נראה איפה אנחנו עובדים ברשימת העבודה:

  • אובייקט לצורת הריבוע
  • נפילה
  • תנועה ימינה ושמאלה כשלוחצים על החצים
  • להערם בסוף הנפילה
  • להעלים שורות שלמות
  • לתכנת את שאר הצורות
  • לסובב את הצורות כשלוחצים על חץ למעלה

אובייקט ערימה

איזה מין אובייקט (או אובייקטים) צריכים להיות בערימה של הצורות שנחתו?

  • צורה שכבר נחתה כבר לא מגיבה יותר ללחיצה על החצים, אלא נשארת במקומה.
  • צורות שנחתו מגבילות את התנועה של הצורה הנופלת, ממש כמו הגבול.
  • כששורה מתמלאת בצורות שנחתו, כל הריבועים בשורה הזאת נעלמים.

לכן אנחנו יכולים לדעת שני דברים לגבי האובייקט של הערימה:

  1. הוא יציב.
  2. הדמות שלו היא ריבוע בגודל 16 פיקסלים, כי כשמשלימים שורה מעלימים ריבועים כאלה (ולא צורות שלמות כמו הצורות שנופלות).

בנוסף, אנחנו רוצים שהריבוע יראה כמו הריבועים של הצורה הנופלת. ככה מה שהמשתמש יראה זה צורה נוחתת, ולא צורה משתנה למשהו אחר.

אם ככה, מה שצריך לעשות זה:

  1. ליצור דמות חדשה של ריבוע בגודל 16 פיקסלים, שנראית כמו הריבועים של דמות הצורה. הדרך הכי נוח לעשות את זה היא לשכפל את דמות הצורה (להקליק עליה בכפתור הימני של העכבר ואז לבחור Duplicate. אז לעבור לעריכת הדמות החדשה, ושם להקטין את הדף לחצי מהגודל באמצעות Resize Canvas מתפריט Transform. צריך גם לזכור לסמן שהיא לא שקופה (Transparent).
  2. ליצור אובייקט חדש בשם pile ולתת לו את הדמות החדשה. לסמן שהוא יציב (Solid).
  3. ללכת לארוע שבו הצורה נוחתת (צלצול שעון מעורר תמונה:gmaker_alarm.png) ושם ליצור ארבעה מופעים של אובייקט הערימה תמונה:gmaker_create_instance.png במקום של הצורה (לסמן Relative):
    • אחד בנקודה 0,0
    • אחד בנקודה 0,16
    • אחד בנקודה 16,0
    • אחד בנקודה 16,16

רשימת הפעולות המלאה צריכה להראות ככה:

שימו לב ששיניתי קצת את הסדר, כי צריך ליצור את הריבועים בערימה לפני שמחזירים את הצורה למעלה. אחרת לא נוכל ליצור אותם במקום שאליו הגיעה הצורה.


תמונה:gmaker_test_save.png אחרי שהצורה נוחתת היא נראית כאילו היא נשארת במקום שבו היא נחתה, וצורה חדשה יורדת מלמעלה. הצורות שנוחתות נערמות אחת על השניה.



 גובה הערימה

כשהערימה גבוהה וממלאת את אזור המשחק עד למעלה, המשחק נגמר. "למעלה" זה אומר בגובה שבו הצורה מתחילה ליפול, כלומר: במיקום ההתחלתי של הצורה.

המיקום ההתחלתי של אובייקט נשמר בשני משתנים:

  • xstart (אִיקְס סְטַרְט) - x בהתחלה.
  • ystart (וּוַי סְטַרְט) - y בהתחלה.

כדי לדעת אם הערימה גבוהה מדי, צריך לבדוק את הגובה של אובייקט הערימה ביחס ל- ystart. את זה צריך לעשות כשהצורה נוחתת, כלומר: כשהיא הופכת לחלק מהערימה.

לכן נלך לאובייקט הערימה ונוסיף לו ארוע יצירה תמונה:gmaker_create.png. בארוע הזה נשים את הפעולה לבדיקת ערך של משתנה תמונה:gmaker_if_var.png ובה נבדוק אם המשתנה y שווה למשתנה shape.ystart. אם כן, זה אומר שהערימה הגיעה עד לגובה שממנו הצורה מתחילה ליפול.

אם פעולת הבדיקה נותנת תוצאה חיובית, המשחק צריך להגמר. לכן נשים שם בלוק, ובתוכו הפעולות להצגת טבלת האלופים תמונה:gmaker_highscore.png ולהתחלה מחדש של המשחק תמונה:gmaker_restart.png .


תמונה:gmaker_test_save.png נסו לערום צורות עד למעלה.



[ סקירת העבודה

יצרנו משחק בסיסי עובד. האם נוכל להמשיך ולהוסיף לו דברים עד שיהיה לנו משחק טטריס שלם? או שנצטרך לשנות חלק מהדברים שכבר עשינו לפני שנוכל להמשיך הלאה?

בואו נעבור על מה שכבר עשינו ברשימת העבודה, וננסה לראות אם יש פריטים שנצטרך לשנות, או שיספיק לנו רק להוסיף דברים חדשים. רשימת העבודה כרגע היא:

  • אובייקט לצורת הריבוע
  • נפילה
  • תנועה ימינה ושמאלה כשלוחצים על החצים
  • להערם בסוף הנפילה
  • להעלים שורות שלמות
  • לתכנת את שאר הצורות
  • לסובב את הצורות כשלוחצים על חץ למעלה

נעבור עליה פריט-פריט:

  • אובייקט לצורת הריבוע - האובייקט shape מתאים גם לצורות האחרות, מלבד הדמות שלו. אבל דמות אפשר לשנות (בפעולת שינוי דמות תמונה:gmaker_change_sprite_button.png) אז זו לא בעיה. נצטרך רק להוסיף לו פעולה של סיבוב כשהמשתמש לוחץ על חץ למעלה.
  • נפילה - כאן לא נצטרך לשנות כלום. כל הצורות נופלות באותה דרך.
  • תנועה ימינה ושמאלה - גם כאן לא נצטרך לשנות כלום, כי התנועה שתכנתנו מתאימה לכל הצורות.
  • להערם בסוף הנפילה - כל הצורות יצטרכו להפוך לאובייקט ערימה. אבל בכל זאת, מה שתכנתנו לא מתאים לכל הצורות:
    1. הצבע של דמות אובייקט הערימה צריך להיות כמו הצבע של דמות הצורה.
    2. המיקומים שבהם צריך ליצור את המופעים של אובייקט הערימה צריכים להתאים לצורה שנוחתת.

זאת אומרת שאובייקט הצורה והתנועה שלו יכולים להשאר כפי שהם, אבל את הנחיתה של הצורה נצטרך לשנות אם נרצה שהיא תתאים לעוד צורות.

איך לשנות את הנחיתה? מה בדיוק צריך לעשות אחרת? בשביל זה צריך להיות מסוגלים לבדוק את השינויים שאנחנו עושים; רק ככה נוכל לנסות לשנות דברים ולראות אם השינויים באמת עובדים. ובשביל לבדוק איך לשנות את הנחיתה כדי שהיא תתאים לעוד צורות, נצטרך עוד צורות
 

איך להוסיף עוד צורות

אפשרות אחת לתכנת עוד צורות היא להכין אובייקט לכל צורה. אז יהיו לנו 7 אובייקטים שדומים זה לזה כמעט בכל. ההבדל היחיד יהיה בנחיתה שלהם: בצבע ובמיקום מופעי הערימה שיוצר כל אחד משבעת האובייקטים.

זה פתרון לא טוב. יש בו שתי בעיות עיקריות:

  1. כל פעם שנרצה להוסיף משהו למשחק או לשנות משהו, נצטרך לעשות את זה בשבעה אובייקטים שונים.
  2. מדי פעם אנחנו עושים שגיאות. ככל שיש יותר אובייקטים, ככה יש יותר מקומות שאנחנו עלולים להכניס בהם שגיאות. וככה גם יהיה קשה יותר למצוא את השגיאות.

בדרך כלל עדיף שדברים זהים יופיעו רק פעם אחת. לכן עדיף שיהיה לנו רק אובייקט צורה אחד. נוכל להשתמש בו לכל שבע הצורות: פשוט נחליף לו את הדמות ככה שהוא יציג כל פעם צורה אחרת. את הנחיתה של הצורה נצטרך לתכנת בדרך שתתאים לכל שבע הצורות.

איך נתכנת את הנחיתה? כרגיל, בשביל לדעת את זה נצטרך לבדוק את זה ובשביל לבדוק את זה נצטרך לתת לאובייקט הצורה דמות אחרת ולנסות לשנות אותו כך שיעבוד גם איתה. נבחר בדמות הפשוטה ביותר מבין 6 הדמויות שנשארו: דמות הקו הארוך (I).

 צורת קו

כדי להתאים את אובייקט הצורה גם לדמות I, נשנה את הדמות שלו בחלון תכונות האובייקט. ככה נוכל לבדוק שהשינויים שנעשה יתאימו לצורת I. כמובן שבסוף נצטרך להגריל דמות באופן אקראי, אבל בואו נשאיר את זה להמשך (ובינתיים נכתוב את זה ברשימת העבודה).


תמונה:gmaker_test_save.png עכשיו נופלים קוים ארוכים אדומים, אבל כשהם נוחתים הם הופכים לערימה של ריבועים תכולים.


יש שני דברים שנצטרך לשנות:

  1. המיקום של מופעי הערימה שנוצרים כשהצורה נוחתת.
  2. הצבע שלהם.

בואו נדחה כרגע את הטיפול בצבע (נכתוב את זה ברשימת העבודה) ונטפל קודם במיקום.

 מיקום מופעי הערימה

כשהקו נוחת, צריך ליצור טור של ארבעה מופעי ערימה אחד מתחת לשני. איך אפשר לתכנת את הצורה ליצור גם טור של מופעי ערימה (כשנוחת קו) וגם ריבוע של מופעי ערימה (כשנוחת ריבוע)? נצטרך להסתכל בדמות הנוכחית של הצורה, ולפי הדמות ליצור את מופעי הערימה במקומות המתאימים.

יש שני משתנים שיעזרו לנו:

  • sprite_width (סְפְּרַייט וּוִידְס') - רוחב הדמות.
  • sprite_height (סְפְּרַייט הַייט) - גובה הדמות.

מה שנעשה זה לעבור על הדמות לכל הרוחב וכל האורך, ולמלא אותה במופעי ערימה. כל הרוחב של הקו זה בדיוק כמו מופע אחד של הערימה, וכל הגובה של הקו מספיק לארבעה מופעי ערימה. ככה שאחרי שנעבור על הרוחב והאורך של דמות הקו יהיה לנו טור גבוה של מופעי ערימה. לעומת זאת, דמות הריבוע היא ברוחב שני מופעי ערימה ובגובה שני מופעי ערימה, לכן אחרי שנעבור על דמות הריבוע נקבל שתי שורות שבכל אחת שני מופעי ערימה.

איך עוברים על דמות הצורה לאורך ולרוחב? נתחיל ברוחב - נלך לנחיתה של הצורה (בארוע צלצול שעון מעורר של אובייקט הצורה), נמחק את ארבע הפעולות שיוצרות מופעי ערימה, ובמקומן נוסיף פעולות אחרות:

  1. תמונה:gmaker_set_var.png נאתחל ל-0 משתנה בשם pile_x. המשתנה הזה יסמן את קורדינטת x של מופע הערימה שאנחנו יוצרים. כל פעם נגדיל אותו ב-16, עד שנכסה את כל הרוחב של דמות הצורה.
  2. תמונה:gmaker_repeat_button.png נוסיף פעולת חזרה כדי ליצור לולאה שתחזור לכל רוחב הצורה. מספר הפעמים שהלולאה צריכה לחזור הוא כמו מספר מופעי הערימה שנכנסים ברוחב דמות הצורה, כלומר: רוחב הדמות לחלק ל-16. לכן נכתוב בחלון אפשרויות פעולת החזרה sprite_width / 16.
  3. בלולאה יהיו כמה פעולות, לכן נשים אחרי פעולת החזרה את הפעולות לתחילת בלוק ולסיום בלוק.
  4. תמונה:gmaker_create_instance.png בתוך הבלוק נשים את הפעולה ליצירת מופע כדי ליצור מופע של הערימה. בשדה x נכתוב pile_x, ובשדה y נכתוב בינתיים 0. כמובן שנסמן Relative כדי שהערימה תיווצר במקום שבו הצורה.
  5. תמונה:gmaker_set_var.png נגדיל את pile_x ב-16 (לסמן Relative) כדי להתקדם הלאה לרוחב השורה.

תמונה:gmaker_test_save.png כשהצורה נוחתת, השורה העליונה שלה תהפוך לערימה. שנו את דמות הצורה חזרה לריבוע כדי לבדוק את זה גם לדמות הריבוע וגם לדמות הקו.



עכשיו צריך לעשות אותו הדבר גם לגובה. למעשה לא בדיוק אותו הדבר, כי לא נסתפק רק בעמודה השמאלית ובשורה העליונה: אנחנו רוצים את כל השורות וכל העמודות.

 לולאה כפולה

הצלחנו לעבור על שורה אחת של הדמות. בשביל לעבור על כל השורות, נצטרך עוד לולאה: לולאה שתחזור שוב ושוב על כל הלולאה של השורה. איך נעשה את זה?

  1. תמונה:gmaker_set_var.png לפני כל הפעולות שהוספנו קודם, נאתחל ל-0 משתנה בשם pile_y. המשתנה הזה יסמן את קורדינטת y של מופע הערימה שאנחנו יוצרים. כל פעם נגדיל אותו ב-16, עד שנכסה את כל הגובה של דמות הצורה.
  2. תמונה:gmaker_repeat_button.png נוסיף פעולת חזרה כדי ליצור לולאה שתחזור לכל גובה הצורה. מספר הפעמים שהלולאה צריכה לחזור הוא כמו מספר מופעי הערימה שנכנסים בגובה דמות הצורה, כלומר: גובה הדמות לחלק ל-16. לכן נכתוב בחלון אפשרויות פעולת החזרה sprite_height / 16.
  3. בלולאה יהיו הרבה פעולות, לכן נשים אחרי פעולת החזרה את הפעולה לתחילת בלוק. את הפעולה לסיום בלוק נשים אחרי סוף הבלוק של הלולאה שתכנתנו קודם. ככה הלולאה שעוברת על הדמות לגובה תחזור שוב ושוב על הלולאה שעוברת על שורה אחת לרוחב. כלומר: המעבר על כל השורה יתבצע שוב ושוב, מהשורה העליונה ועד לתחתונה.
  4. תמונה:gmaker_create_instance.png נשנה את הפעולה ליצירת מופע: בשדה y נכתוב pile_y.
  5. תמונה:gmaker_set_var.png אחרי סוף הלולאה שעוברת על שורה, נגדיל את pile_y ב-16 (לסמן Relative) כדי לרדת לשורה הבאה.

תמונה:gmaker_test_save.png כשהצורה נוחתת, נוצרת ערימה שמכסה את כל הצורה בדיוק. כדאי לבדוק את זה גם כשהדמות היא O וגם כשהדמות היא I.



נסתכל רגע מה עשינו כאן: יצרנו לולאה בתוך לולאה - לולאה כפולה. הלולאה החיצונית עוברת מהקצה העליון של הצורה עד התחתית שלה, כל פעם קצת יותר נמוך. בכל חזרה כזאת, הלולאה הפנימית עוברת מהצד השמאלי של הצורה עד הצד הימני. בתמונה משמאל אפשר לראות את הסדר שבו זה קורה כאשר הצורה היא בדמות הריבוע.

 צורות אחרות

הצלחנו ליצור ערימה מתאימה גם לדמות O וגם I. מה לגבי הדמויות האחרות? בואו נראה: נשנה את הדמות של אובייקט הצורה ל-T.


תמונה:gmaker_test_save.png מה קורה כשהצורה נוחתת?



הערימה אמנם מכסה את כל הצורה, אבל היא מכסה גם אזורים שקופים. צריך למחוק את מופעי הערימה שנוצרים באזורים השקופים של הצורה.

איך נדע איזה מופעי ערימה נוצרים באזורים השקופים של הצורה? יש דרך פשוטה: לבדוק אם הם מתנגשים בצורה. מופע ערימה שנמצא בחלק השקוף של הצורה, לא מתנגש בה.

איפה נבדוק את ההתנגשות? זה צריך להיות אחרי שמופע הערימה נוצר, אבל לפני שהצורה חוזרת למעלה ומתחילה שוב ליפול. לכן הכי נוח לעשות את זה בארוע היצירה של אובייקט הערימה. תמונה:gmaker_create.png

בארוע הזה צריך לבדוק אם מופע הערימה מתנגש באובייקט צורה. בשביל זה נשתמש בפעולה לבדיקת התנגשות באובייקט, שנמצאת בלשונית control: תמונה:gmaker_if_collision_object.png. הפעולה הזאת דומה לפעולה לבדיקת התנגשות, אבל היא בודקת התנגשות רק במופעים של אובייקט מסוים. מאחר שאנחנו רוצים לבדוק התנגשות רק באובייקט הצורה (ולא במופעי ערימה אחרים), הפעולה הזאת מתאימה לנו בדיוק.

בחלון אפשרויות הפעולה נבחר באובייקט הצורה בשדה object. כדי לבדוק התנגשות במיקום הנוכחי של הערימה, נשאיר את השדות x ו-y על 0 ונסמן Relative.

מה נעשה אם יש התנגשות בצורה? כלום. אנחנו מתעניינים רק במקרה שבו אין התנגשות בצורה, כי אז צריך למחוק את המופע. לכן בחלון אפשרויות הפעולה של פעולת הבדיקה נסמן את תיבת הסימון השניה: NOT (נוֹט) - לא.

NOT מסמן להפוך את התוצאה של פעולת הבדיקה. כלומר, במקום שהפעולה הבאה תתבצע רק אם יש התנגשות, היא תתבצע רק אם אין התנגשות. לכן עכשיו נוכל לשים אחרי פעולת הבדיקה את פעולת המחיקה תמונה:gmaker_destroy_button.png.

מאחר שאנחנו לא רוצים להמשיך לפעולות הבאות, נשים גם את פעולת היציאה מהארוע תמונה:gmaker_exit.png, ונקיף את שתי הפעולות האלה בבלוק.


תמונה:gmaker_test_save.png מה קורה עכשיו כשהצורה נוחתת? נסו להחליף את דמות אובייקט הצורה גם לשאר הצורות, כדי לבדוק אם הערימה שנוצרת מתאימה לצורה.



 המשך העבודה

הצלחנו לתכנת את הנחיתה בדרך שתתאים לכל הצורות, ככה שלא צריך בשבילן אובייקטים נפרדים. בואו נראה מה נשאר לעשות עכשיו:

  • אובייקט לצורת הריבוע
  • נפילה
  • תנועה ימינה ושמאלה כשלוחצים על החצים
  • להערם בסוף הנפילה
  • לתכנת את שאר הצורות
  • לבחור את צבע הערימה לפי הצורה
  • לבחור צורה באופן אקראי
  • לסובב את הצורות כשלוחצים על חץ למעלה
  • להעלים שורות שלמות
 

בחירת דמות לערימה

כשצורה נוחתת, הערימה שנוצרת צריכה להיות באותו צבע כמו הצורה. לכן נצטרך 7 דמויות לערימה - דמות לכל אחת מהצורות. די קל ליצור את התמונות לדמויות האלה לבד, אם משכפלים את הדמויות של הצורות ועורכים אותן קצת. למי שלא רוצה להתעסק עם זה, הכנתי כאן דמויות:

תמונה:tetris_pile_aqua.png תמונה:tetris_pile_red.png תמונה:tetris_pile_gray.png תמונה:tetris_pile_yellow.png תמונה:tetris_pile_purple.png תמונה:tetris_pile_blue.png תמונה:tetris_pile_green.png

(חשוב לזכור שכל הדמויות האלה צריכות להיות לא שקופות Transparent.)

אחרי שמוסיפים את הדמויות למשחק, אפשר לבחור ביניהן. המקום המתאים לעשות את זה הוא ארוע היצירה של אובייקט הערימה, מיד אחרי הבלוק של בדיקת ההתנגשות בצורה. שם נוכל לבדוק מה הדמות של הצורה, ולפי זה לקבוע את הדמות של מופע הערימה שנוצר.

 משתנה הדמות

הדמות של מופע נקבעת במשתנה שנקרא sprite_index (סְפְּרַייט אִינְדֵקְס) - סמן דמות. (ממש כמו שהמיקום נקבע במשתנים x ו-y.) לכן נצטרך לשים את הפעולה לבדיקת ערך של משתנה תמונה:gmaker_if_var.png ובה נבדוק את הערך של סמן הדמות של הצורה - המשתנה shape.sprite_index. בשדה value נכתוב את שם הדמות של צורת הקו. (אני קראתי לדמות הזאת I, אבל אם נתת לה שם אחר אז צריך לכתוב את השם שבו השתמשת.)

אם הבדיקה נותנת תוצאה חיובית, צריך לשנות את הדמות של מופע הערימה לריבוע האדום. אפשר לעשות זאת בפעולת שינוי הדמות תמונה:gmaker_change_sprite_button.png, אבל אפשר גם פשוט לשנות את המשתנה sprite_index. בשביל זה צריך לשים את הפעולה לקביעת הערך של משתנה תמונה:gmaker_set_var.png ובה לשנות את המשתנה sprite_index. הערך שצריך לתת לו הוא השם שנתת לדמות הריבוע האדום (אצלי זה pile_red).


תמונה:gmaker_test_save.png קבעו את דמות הצורה לקו האדום כדי לבדוק שהערימה שנוצרת באמת אדומה. קבעו גם דמויות אחרות - שם הערימה עדיין תכולה.



עכשיו תורך:

להוסיף פעולות לבדיקת משתנה ולקביעת משתנה גם עבור הדמויות בשאר הצבעים.


הרבה עבודה לתכנת את כל הבדיקות וקביעות המשתנים האלה, לא? ודאי ישמח אותך לדעת שיש דרך נוחה יותר לעשות את זה. אבל בשביל זה צריך לתכנת את הפעולות שיתבצעו בצורה שונה: בקוד של שפת תכנות.
 
 

שפת תכנות

מחשבים לא מבינים עברית. בתוך נבכי המחשב מתרוצצים להם אותות חשמליים שהם השפה היחידה שהמחשב באמת מבין. בני אדם נוהגים לייצג את האותות האלה בסימנים 1 (כשעובר זרם חשמלי) ו-0 (כשלא עובר זרם) ולקרוא להם שפת מכונה. כדי לתכנת את המחשבים הראשונים, המתכנתים היו צריכים ללמוד שפת מכונה ולהכניס למחשב סימנים של 0 ו-1 (בדרך כלל באמצעות ניקובים בכרטיסי קרטון, שגרמו למתגים במחשב לזוז). זה היה כרוך בהרבה מאוד עבודה.

עם הזמן מצאו פתרון נוח יותר: במקום לכתוב 0 ו-1 (או לנקב כרטיסים), המתכנתים כתבו מלים באנגלית וסימני חשבון, והשתמשו בתוכנה מיוחדת שהפכה את מה שהם כתבו לשפת מכונה. כמובן שהתוכנה הזאת לא הבינה את כל המלים באנגלית ואת כל הסימנים שיש; היא הבינה רק דברים מסוימים, והמתכנתים למדו לכתוב רק מה שהתוכנה יודעת להפוך לשפת מכונה. ככה נוצרו מעין שפות דומות קצת לאנגלית וקצת לחשבון, שנקראות שפות תכנות. לדברים שכותבים בשפת תכנות נהוג לקרוא קוד - אולי כי התוכנה מפענחת את הקוד לשפת מכונה, ואולי כי זה נראה כמו קוד סתרים למי שלא מכיר את שפת התכנות...

ב-Game Maker המשיכו עם הרעיון הזה עוד צעד אחד קדימה: במקום לתכנת בשפת מכונה או שפת תכנות, Game Maker מראה איזה פעולות אפשר לבצע, ואפשר פשוט לגרור אותן לרשימת הפעולות.

ועדיין, לא לכל דבר יש פעולה מתאימה ב-Game Maker. יש דברים שאפשר לתכנת רק באמצעות שפת תכנות. לכן יש ב-Game Maker אפשרות לתכנת גם בשפת תכנות. שפת התכנות של Game Maker נקראת GML, שזה ראשי תיבות של Game Maker Language - שפת Game Maker. התחביר שלה דומה לתחביר של שפות תכנות אחרות כמו C, C++, Java, JavaScript, Perl ואחרות. ככה שאם מכירים את GML מקבלים בסיס שעוזר ללמוד שפות תכנות אחרות בעתיד.

 פעולת קוד

כדי לתכנת בשפת GML משתמשים בפעולה לביצוע קוד, שנמצאת בלשונית control: תמונה:gmaker_code_button.png. בפעולה הזאת אפשר לכתוב קוד שיבצע כל דבר שאפשר לבצע בפעולות האחרות, ואפשר לכתוב גם דברים נוספים שאי אפשר לעשות באמצעות הפעולות האחרות. אנחנו נתחיל לאט לאט: קודם נכתוב קוד שעושה דברים שכבר הכרנו, ורק אחר כך נכתוב קוד שעושה דברים חדשים.

נתחיל במשהו פשוט: הפעולה לקביעת הערך של משתנה תמונה:gmaker_set_var.png. נלך לארוע היצירה של אובייקט הערימה, לפעולה שנותנת למשתנה sprite_index את הערך pile_red. נמחק אותה, במקומה נשים פעולת קוד תמונה:gmaker_code_button.png.

חלון אפשרויות הפעולה נראה שונה מאוד לעומת הפעולות האחרות: רוב החלון הוא אזור לבן, שבו אפשר לכתוב את הקוד. נכתוב שם את הקוד הזה:

sprite_index = pile_red;

מה זה אומר?

  • הסימן = אומר שזו פעולת קביעת ערך של משתנה, או הצבה, כי "מציבים" ערך אל תוך משתנה.
  • משמאל לסימן = כותבים את שם המשתנה שמציבים אליו. במקרה שלנו זה המשתנה sprite_index.
  • מימין לסימן = כותבים את הערך שמציבים למשתנה. במקרה שלנו זה pile_red.
  • בסוף הפעולה כותבים את הסימן נקודה-פסיק (;) כדי לסמן איפה הפעולה נגמרת. בקוד אפשר לכתוב הרבה פעולות אחת אחרי השניה, ולכן צריך לסמן איפה כל פעולה נגמרת. לכל פעולה כזאת (שנגמרת בנקודה-פסיק) קוראים "פקודה" או "הוראה", כי היא אומרת למחשב מה לעשות.

בין החלקים השונים בקוד מותר לכתוב רווחים, אם רוצים. (כדאי לרצות לכתוב רווחים - זה מקל על הקריאה.)

אחרי שגמרת לכתוב את הקוד הזה, צריך ללחוץ על הכפתור עם ה-וי הירוק (שנמצא למעלה משמאל) כדי לסגור את החלון של הקוד. עכשיו פעולת הקוד תופיע ברשימת הפעולות של הארוע. אם מצביעים עליה בעכבר ומחכים רגע, תצוץ חלונית שמראה את הקוד.


תמונה:gmaker_test_save.png קבעו את דמות הצורה לקו האדום כדי לוודא שהקוד עובד. אם יש תקלות, נסו לקרוא בעיון את הקוד - לפעמים נופלות שגיאות הקלדה, ואז המחשב לא מצליח להבין את הקוד. אם נתת לדמות הריבוע האדום שם אחר, צריך לכתוב את השם שנתת במקום pile_red.



עכשיו תורך:

להחליף לפעולות קוד גם את שאר הפעולות של קביעת ערך משתנה.



 פעולות אם-אז בקוד

נמשיך לקוד קצת יותר מורכב: פעולות בדיקה, או "אם-אז". ניקח את הפעולה לבדיקת ערך של משתנה תמונה:gmaker_if_var.png. בקוד היא לא מופיעה לבד, אלא עם הפקודה או הבלוק שאחריה. ניקח פעולת בדיקה עם בלוק פשוט אחריה:

בקוד, נכתוב את הפעולות האלה ככה:

if (shape.sprite_index == I)
{
    sprite_index = pile_red;
}

נעבור על הקוד לאט לאט כדי להבין מה כתוב בו:

  • if היא מילת מפתח - מילה שמסמנת פקודה. כמו שהסימן = מסמן פקודת הצבה, ככה המילה if מסמנת את הפקודה המקבילה לפעולת הבדיקה. משמעות המילה if באנגלית היא פשוט "אם". הפקודה שהיא מסמנת היא פקודת אם-אז.
  • (shape.sprite_index == I) הוא הביטוי שבודקים בפקודה. בפקודת if, מה שבודקים תמיד מופיע בתוך סוגריים אחרי המילה if. אפשר לכתוב כל מיני ביטויים, כמו למשל: 1 < 2. פקודת if בודקת אם הביטוי נכון או לא, ואם כן אז הבלוק שאחריה יתבצע. לגבי הביטוי שבתוך הסוגריים:
    • == מסמן השוואה בין שני דברים. ראינו כבר שסימן = אחד מסמן פעולת הצבה. אם רוצים לכתוב ביטוי שאומר אם שני דברים שווים זה לזה, משתמשים בסימן == (בלי רווח בין שני סימני השוויון).
    • shape.sprite_index הוא הערך של המשתנה sprite_index של אובייקט shape, בדיוק כמו בפעולות הרגילות.
    • I מסמן את הדמות של הקו הארוך, בדיוק כמו בפעולות הרגילות.
  • סימני הצומדיים { ו-} הם פשוט תחילת הבלוק וסוף הבלוק.
  • sprite_index = pile_red; היא פקודת הצבה, כמו שראינו קודם. שימו לב ששמתי לפניה רווחים - זה מסמן שהיא בתוך הבלוק. למחשב לא אכפת אם נשים רווחים או לא, אבל בכל זאת כדאי להקפיד לשים רווחים לפני פקודות בתוך בלוק כדי שיהיה קל לראות איפה הבלוק מתחיל ואיפה הוא נגמר. זה חשוב במיוחד במקרה של בלוק בתוך בלוק (למשל אם עושים לולאה כפולה).

בואו נשתמש בקוד הזה: ניצור פעולת קוד ובה נכתוב את הקוד הזה. נשים אותה במקום פעולת הבדיקה ופעולת הקוד להצבה שהשתמשנו בהן קודם למקרה שהצורה היא בדמות הקו.

חשוב: אם נתת שמות אחרים לדמות הקו או לדמות הריבוע האדום, צריך לשנות את הקוד כדי שיופיעו בו השמות האלה, ולא השמות שאני בחרתי.


תמונה:gmaker_test_save.png צריך לבדוק שהנחיתה עדיין עובדת, גם עם הקו וגם עם צורות אחרות.



אפשר להחליף את כל פעולות הבדיקה בצורה דומה, ובמקום כל אחת מהן לשים פעולת קוד. אבל אין צורך בכך: בפעולת קוד אחת אפשר לשים הרבה פקודות, אחת אחרי השניה. את כל פעולות הבדיקה אפשר להחליף בקוד הזה:

if (shape.sprite_index == I)
{
    sprite_index = pile_red;
}
if (shape.sprite_index == T)
{
    sprite_index = pile_gray;
}
if (shape.sprite_index == L)
{
    sprite_index = pile_yellow;
}
if (shape.sprite_index == J)
{
    sprite_index = pile_purple;
}
if (shape.sprite_index == S)
{
    sprite_index = pile_blue;
}
if (shape.sprite_index == Z)
{
    sprite_index = pile_green;
}

אבל גם בזה אין צורך. את כל הפקודות if האלה אפשר להחליף בפקודה אחת.

 פקודת הבחירה

פקודת הבחירה switch היא פקודת בקרה: היא קובעת אם פקודות אחרות יתבצעו או לא, בדומה לפקודת if. אבל היא קובעת את זה בצורה שונה: היא בודקת מה הערך של ביטוי, ולפי זה בוחרת לבצע פקודה אחת מתוך רשימה של פקודות. ככה נוכל לבדוק את הערך של המשתנה shape.sprite_index, ולפיו לבחור איזו פקודת הצבה לבצע.

הקוד של פקודת הבחירה נראה ככה:

switch (shape.sprite_index)
{
case I:
    sprite_index = pile_red;
    break;
case T:
    sprite_index = pile_gray;
    break;
case L:
    sprite_index = pile_yellow;
    break;
case J:
    sprite_index = pile_purple;
    break;
case S:
    sprite_index = pile_blue;
    break;
case Z:
    sprite_index = pile_green;
    break;
default:
    sprite_index = pile_aqua;
}

נעבור על הקוד כדי להבין אותו:

  • switch היא מילת מפתח שמסמנת פקודת בחירה: היא קובעת איזה פקודות לבצע לפי הערך שבודקים. הפקדות שהיא תבחר מופיעות בבלוק שאחריה.
  • (shape.sprite_index) הביטוי בסוגריים שמופיע אחרי המילה switch הוא הערך שבודקים אותו. במקרה שלנו, בודקים את הערך של shape.sprite_index ולפיו בוחרים איזה פקודות לבצע.
  • case I: היא תווית שמסמנת "כאן הפקודות שצריך לבצע במקרה שהערך שבודקים הוא I". המילה case היא מילת מפתח שמסמנת לבדוק אם מה שכתוב אחריה שווה לביטוי בסוגריים שאחרי המילה switch. אם הם שווים, יבוצעו כל הפקודות שאחרי הנקודותיים.
  • sprite_index = pile_red; היא פקודת הצבה. היא תתבצע אם הביטוי שבודקים (shape.sprite_index) שווה לתווית שמעליה (I). אמנם כתבנו רק פקודה אחת לביצוע בתווית הזו, אבל אפשר לכתוב כמה פקודות, ואז כל הפקודות האלה יבוצעו אם הביטוי של ה-switch שווה לתווית.
  • break; היא פקודת בקרה מיוחדת: היא גורמת להפסיק את ביצוע הפקודות שאחריה, עד לסוף הבלוק. זה דומה קצת לפעולת היציאה מהארוע תמונה:gmaker_exit.png, אבל break לא מוציאה מהארוע כולו אלא רק מהבלוק של פקודת switch. אם לא היינו כותבים break, המחשב היה ממשיך לבצע את הפעולות הבאות (למרות שהן מופיעות בתוויות אחרות).
  • default: היא התווית האחרונה, שמסמנת מה לעשות אם אף אחת מהתוויות האחרות לא מתאימה. לא חייבים לכתוב את התווית default, אבל בדרך כלל כדאי - יכול להיות שיקרה משהו בלתי צפוי ואז כדאי שנטפל גם במקרה הזה.


{{{גודל}}}

כדאי לדעת:

קצת מסובך? לא לדאוג - רוב הדברים שנכתוב בקוד יהיו יותר פשוטים מזה. ובמשך הזמן דברים שנראים עכשיו מסובכים ייראו פתאום מובנים וברורים.(למרות שמדובר בקוד פשוט מאוד)


עכשיו נעתיק לפעולת קוד את הקוד שלמעלה עם פקודת ה-switch, ונמחק את כל פעולות הבדיקה וההצבה שהשתמשנו בהן קודם:

חשוב: אם נתת שמות אחרים לדמויות של הצורה או של הערימה, צריך לשנות את הקוד כדי שיופיעו בו השמות האלה ולא השמות שאני בחרתי.



תמונה:gmaker_test_save.png תנו לאובייקט הצורה דמות שונה בכל פעם כדי לוודא שהנחיתה עובדת בכל הדמויות. מה קורה כשנותנים לו את הדמות של הגבול?



{{{גודל}}}

כדאי לדעת:

כשכותבים קוד צריכים להקליד הרבה יותר מאשר כשמשתמשים בפעולות הרגילות. אבל מהר מאוד מתרגלים לזה. רוב המתכנתים מקלידים מהר מאוד באנגלית למרות שהם מעולם לא למדו בצורה מסודרת להקליד. הזריזות והדיוק באים עם הזמן והניסיון.



 המשך העבודה

התקדמנו הרבה בידע, אבל השלמנו רק סעיף אחד ברשימת העבודה:

  • אובייקט לצורת הריבוע
  • נפילה
  • תנועה ימינה ושמאלה כשלוחצים על החצים
  • להערם בסוף הנפילה
  • לתכנת את שאר הצורות
  • לבחור את צבע הערימה לפי הצורה
  • לבחור צורה באופן אקראי
  • לסובב את הצורות כשלוחצים על חץ למעלה
  • להעלים שורות שלמות
אל תחששו להתקדם לסעיפים הבאים גם אם לא הכל ברור - רוב מה שנעשה בהמשך יהיה פשוט יותר.
 

בחירת דמות אקראית

ראינו שאפשר להחליף דמות באמצעות הצבה תמונה:gmaker_set_var.png למשתנה sprite_index. אנחנו רוצים להגריל באופן אקראי אחת משבע הדמויות של הצורות. אבל אנחנו מכירים את הדמויות האלה רק בשמות שלהן: O, I, T וכן הלאה. אם נגריל מספר אקראי באמצעות הפונקציה random() זה לא יעזור לנו לקבל שם של דמות.

מה עושים?

אפשרות אחת היא להשתמש שוב בפקודת switch, ולבחור דמות לפי התוצאה של הפונקציה random().

אבל יש דרך פשוטה יותר. ב-Game Maker יש פונקציה שמגרילה באופן אקראי אחד מהפרמטרים שלה: הפונקציה choose() (צ'וּז) - בחר. למשל, אם נכתוב choose(1, 17, 42) אז נקבל אחד מהמספרים 1, 17 או 42, באופן אקראי.

לכן, מה שצריך לעשות זה:

  1. תמונה:gmaker_alarm.png ללכת לנחיתה של אובייקט הצורה (בארוע השעון המעורר). ברשימת הפעולות שם, ללכת לפעולה שבה מזיזים את הצורה חזרה לנקודת ההתחלה. מיד אחריה צריך לשנות לצורה את הדמות.
  2. תמונה:gmaker_set_var.png להוסיף פעולת הצבה ובה להציב במשתנה sprite_index את הערך choose(O, I, T, L, J, S, Z) כדי לקבל אחת מדמויות הצורה באופן אקראי.
  3. תמונה:gmaker_create.png להעתיק את פעולת ההצבה הזאת גם לארוע היצירה של אובייקט הצורה, כדי שגם לצורה הראשונה תהיה דמות אקראית.

תמונה:gmaker_test_save.png עכשיו כל פעם יפלו צורות אחרות, בלי קשר לדמות של אובייקט הצורה.



 סידור מחדש של הנחיתה

יש לנו כבר רשימה ארוכה של פעולות שקורות כשצורה נוחתת. ויהיו לנו עוד בהמשך. כרגע כל הפעולות האלה מופיעות בבלוק של פעולת בדיקת ההפך תמונה:gmaker_else.png שבארוע השעון המעורר תמונה:gmaker_alarm.png. היה נוח יותר אם היה לנו ארוע לנחיתת הצורה, ואז היינו יכולים להעביר את כל הפעולות האלה לשם.

Game Maker מאפשר לנו ליצור ארועים משלנו, ממש כמו הארועים הרגילים. בשביל להעביר את כל הפעולות של הנחיתה לארוע משלנו, צריך:

  1. להקליק על Add event ומבין סוגי הארועים לבחור בסוג ארוע Other תמונה:gmaker_other.png.
  2. בתפריט שצץ לבחור ב-User defined ובתת-תפריט לבחור בפריט הראשון: ארוע משתמש מספר 0. עכשיו נוצר ארוע משלנו, ואפשר לשים בו פעולות כמו בכל ארוע אחר.
  3. ללכת חזרה לארוע השעון המעורר תמונה:gmaker_alarm.png ולבחור את כל הפעולות שבתוך הבלוק של פעולת בדיקת ההפך תמונה:gmaker_else.png.
  4. לקחת אותן משם באמצעות הקשה על Control+X.
  5. ללכת לארוע המשתמש שלנו, להקליק עם העכבר ברשימת הפעולות שלו, ולהקיש Control+V כדי להדביק את כל הפעולות שלקחנו.
  6. כדאי להסביר מה בדיוק הארוע הזה: לפני כל הפעולות שהדבקנו, להוסיף פעולת הערה תמונה:gmaker_comment_button.png ובה לכתוב "ארוע: נחיתת צורה".

העברנו את הפעולות לארוע שלנו, אבל איך Game Maker ידע מתי הארוע שלנו קורה וצריך לבצע את הפעולות האלה? צריך להגיד לו:

  1. לחזור לארוע השעון המעורר תמונה:gmaker_alarm.png ולפעולת בדיקת ההפך תמונה:gmaker_else.png.
  2. את הבלוק שאחרי הפעולה הזו אפשר למחוק, כי נצטרך רק פעולה אחת במקרה שהצורה נוחתת: להגיד ל-Game Maker שקרה הארוע שלנו.
  3. במקום הבלוק, להוסיף פעולת קוד תמונה:gmaker_code_button.png.
  4. בחלון הקוד לכתוב event_user(0);. הפונקציה event_user אומרת ל-Game Maker שעכשיו קורה ארוע שלנו, ושצריך לבצע את הפעולות בארוע הזה. להבדיל מפונקציות כמו random() או choose(), הפונקציה event_user() לא מחזירה ערך אלא מבצעת פעולה.

תמונה:gmaker_test_save.png אם הסידור מחדש הצליח, המשחק אמור להתנהג בדיוק כמו קודם.



עשינו refactoring - לא שינינו את ההתנהגות של התוכנה, אבל סידרנו אותה בצורה יותר ברורה. עכשיו יהיה לנו נוח יותר להמשיך להוסיף לה דברים.

 הצורה הבאה

ברב הגירסאות של טטריס, מראים למשתמש את הצורה הבאה שתיפול כדי שהוא יוכל לתכנן יותר טוב איפה להנחית את הצורה הנופלת. גם אנחנו נעשה ככה.

מה זו הצורה הבאה שמראים למשתמש?
היא נראית כמו הצורות הנופלות, אבל ההתנהגות שלה שונה לחלוטין: היא לא נופלת, והיא לא זזה כשלוחצים על החצים, אלא נשארת במקום קבוע על המסך. לכן הצורה הבאה היא אובייקט נפרד. ניצור בשבילה אובייקט ונקרא לו next_shape. נשים מופע של הצורה הבאה בחדר, ליד אזור המשחק.

בארוע היצירה של הצורה הבאה, צריך לתת לה אחת מהדמויות של הצורות באופן אקראי. (אפשר פשוט להעתיק את פעולת ההצבה תמונה:gmaker_set_var.png מארוע היצירה של אובייקט הצורה.)


תמונה:gmaker_test_save.png ליד אזור המשחק תוצג כל פעם צורה אחרת. אבל היא לא משתנה כשהצורה הנופלת נוחתת, ואין כל קשר בינה לבין הצורה הבאה שנופלת.



נצטרך לשנות את מה שקורה בנחיתה של הצורה הנופלת: במקום שהצורה עצמה תשנה דמות באופן אקראי, נרצה שהצורה הבאה תהיה זו שתשנה דמות, והצורה הנופלת תעתיק את הדמות של הצורה הבאה. בשביל זה צריך:

  1. ללכת לנחיתה של אובייקט הצורה (בארוע המשתמש תמונה:gmaker_other.png). ברשימת הפעולות שם, ללכת לפעולת ההצבה תמונה:gmaker_set_var.png שבה מגרילים דמות חדשה לצורה.
  2. לפתוח את חלון אפשרויות הפעולה באמצעות הקלקה-כפולה על הפעולה. שם לשנות את המופע שהפעולה מתייחסת אליו (Applies to): לבחור ב-object ושם לבחור באובייקט הצורה הבאה. ככה במקום לשנות את הדמות של הצורה הנופלת, הפעולה תשנה את הדמות של הצורה הבאה.
  3. לפני פעולת ההצבה הזאת, להכניס פעולת הצבה נוספת תמונה:gmaker_set_var.png ובה להציב אל המשתנה sprite_index את הערך next_shape.sprite_index. ככה הצורה הנופלת תקבל את הדמות של הצורה הבאה.

תמונה:gmaker_test_save.png עכשיו הצורה שליד אזור המשחק מראה מה תהיה הצורה הבאה שתיפול.



 המשך העבודה

הנה רשימת העבודה המעודכנת:

  • אובייקט לצורת הריבוע
  • נפילה
  • תנועה ימינה ושמאלה כשלוחצים על החצים
  • להערם בסוף הנפילה
  • לתכנת את שאר הצורות
  • לבחור את צבע הערימה לפי צורה
  • לבחור צורה באופן אקראי
  • לסובב את הצורות כשלוחצים על חץ למעלה
  • להעלים שורות שלמות

תמונות לסיבוב

כדי לסובב צורה נצטרך תמונה שלה בכל אחד מהכיוונים. נתחיל ממשהו פשוט: צורת הקו הארוך. נצטרך גם תמונה של קו עומד וגם תמונה של קו שוכב. נוכל לשים את שתי התמונות יחד באותה דמות. בשביל זה נצטרך לערוך את דמות הקו הארוך: לפתוח את חלון תכונות הדמות של הקו (באמצעות הקלקה כפולה על הקו ברשימת המשאבים), וללחוץ על הכפתור Edit Sprite.

עכשיו צריך לשנות את גודל הדמות כדי שהיא תהיה מספיק רחבה להתאים גם לקו שוכב:

  1. להכנס לעריכת התמונה באמצעות הקלקה-כפולה על התמונה.
  2. ללכת לתפריט Transform ולבחור Resize Canvas.
  3. בחלון שנפתח, להוריד את הסימון מתיבת הסימון Keep aspect ratio כדי שנוכל לשנות את הדף מצורת מלבן ארוך לצורת ריבוע.
  4. בשדה הימני של רוחב הדף (Width) לשנות את הערך מ-16 פיקסלים ל-64 פיקסלים. אז להקליק OK.
  5. אם הקו החדש מופיע באמצע התמונה, צריך להזיז אותו, כדי שיתאים למיקום של שריג 16, כמו שאר הצורות. את זה אפשר לעשות באמצעות לחיצה על החצים הכחולים שבסרגל הכלים. אפשר להזיז אותו עד לקצה השמאלי או הימני, ואפשר להזיז אותו רק 8 פיקסלים, ואז הוא יהיה במרחק 16 פיקסלים מהקצה.
  6. ללחוץ על ה-וי הירוק.

עכשיו אפשר להוסיף לדמות עוד תמונה, שבה יהיה הקו השוכב. ל-Game Maker יש דרך מובנית לעשות את זה. בשביל זה צריך:

  1. ללכת לתפריט Animation.
  2. לבחור Rotation Sequence. בתת-תפריט שצץ לבחור Clockwise.
  3. השדה הראשון אומר כמה תמונות יהיו ברצף - לכתוב שם 2 כי הקו יכול להסתובב רק לשתי צורות: שוכב ועומד.
  4. השדה השני אומר איזה זווית צריך לכסות בכל הסיבובים האלה. שם צריך לכתוב 180. (360 מעלות זה סיבוב מלא, אבל במקרה של הקו אחרי חצי סיבוב הוא שוב חוזר להיות קו עומד, אז מספיק 180 מעלות.)

קיבלנו שתי תמונות קו. אפשר להחליף את הסדר שלהן אם רוצים: להקליק על אחת התמונות, ואז ללחוץ על החצים שבצד ימין של סרגל הכלים. ככה אפשר לקבוע איזו תמונה תופיע ראשונה. (בדרך כלל בטטריס הקו מופיע שוכב.)

אחרי שגמרנו, אפשר ללחוץ על ה-וי הירוק ולבדוק מה יצא.


תמונה:gmaker_test_save.png מה קורה כשמוצג קו?



הדמות של הקו מוצגת מונפשת - התמונות שלה מתחלפות במהירות. צריך לשנות את מהירות ההנפשה ל-0 כדי שהדמות לא תזוז. אפשר לעשות את זה באמצעות הפעולה לשינוי דמות תמונה:gmaker_change_sprite_button.png, אבל אפשר גם לקבוע את הערך של המשתנה image_speed (אִימֵג' סְפִּיד) - מהירות תמונה. לכן נלך לארוע היצירה של אובייקט הצורה ונוסיף שם פעולת הצבה תמונה:gmaker_set_var.png שבה נציב במשתנה image_speed את הערך 0. את הפעולה הזאת צריך להעתיק גם לארוע היצירה של אובייקט הצורה הבאה.


תמונה:gmaker_test_save.png עכשיו הקו נראה בסדר.



 הפעלת הסיבוב

הצורה צריכה להסתובב כשהמשתמש לוחץ על חץ למעלה. לכן נוסיף לאובייקט הצורה ארוע הקשה על מקש חץ למעלה (Up) תמונה:gmaker_key_down.png. בארוע הזה צריך לשנות את התמונה המוצגת מתוך התמונות בדמות. גם את זה אפשר לעשות באמצעות הפעולה לשינוי דמות תמונה:gmaker_change_sprite_button.png, אבל גם הפעם נעדיף לעשות את זה באמצעות הצבה למשתנה שקובע את התמונה המוצגת: image_index (אִימֵג' אִינְדֵקְס) - סמן תמונה. לכן נוסיף פעולת הצבה תמונה:gmaker_set_var.png ובה נגדיל את ב-1 את image_index (לסמן Relative).


תמונה:gmaker_test_save.png מה קורה אם לוחצים על חץ למעלה כשנופל קו? ומה קורה כשמופיע הקו הבא?


כשהדמות מתחלפת, זה לא משנה את המשתנה image_index. אם רוצים שהקו יופיע תמיד באותה צורה, צריך להחזיר את הערך של image_index ל-0 כשמחזירים את הצורה למעלה:

  1. ללכת לנחיתה של הצורה (בארוע המשתמש תמונה:gmaker_other.png).
  2. אחרי הפעולה שמחזירה את הצורה חזרה למעלה, להוסיף פעולת הצבה תמונה:gmaker_set_var.png ובה לשנות את image_index חזרה ל-0.

תמונה:gmaker_test_save.png עכשיו הקו תמיד יתחיל ליפול באותה צורה.




{{{גודל}}}

כדאי לדעת:

בפעולה לשינוי דמות תמונה:gmaker_change_sprite_button.png יש שלושה שדות: sprite, subimage, ו-speed. ראינו שאפשר לשנות כל אחד מהם גם באמצעות פעולת הצבה למשתנה תמונה:gmaker_set_var.png: sprite_index, image_speed, ו-image_index.

כלומר: פעולת שינוי הדמות פשוט קובעת את הערכים של שלושת המשתנים האלה.

יש עוד פעולות כאלה, שפשוט קובעות ערכים של משתנים. למשל:

  • תמונה:gmaker_blue_move_button.png פעולת תנועה מדוייקת קובעת את הערך של המשתנים direction ו- speed.
  • תמונה:gmaker_jump_pos.png פעולת העברה למקום אחר קובעת את הערך של המשתנים x ו-y.
  • תמונה:gmaker_set_hspeed.png פעולת קביעת התנועה האופקית קובעת את הערך של המשתנה hspeed.



 התנגשות

בואו נעשה נסיון: תפעילו את המשחק, חכו שייפול קו, סובבו אותו ככה שיעמוד, ואז הזיזו אותו שמאלה עד הסוף - עד לגבול. אז נסו ללחוץ על חץ למעלה כדי לסובב אותו. מה קורה?

אנחנו מרשים לצורה להסתובב גם אם יש התנגשות. צריך לבטל את הסיבוב במקרה של התנגשות:

  1. ללכת לארוע הלחיצה על חץ למעלה תמונה:gmaker_key_down.png.
  2. אחרי פעולת ההצבה, לשים את הפעולה לבדיקת התנגשות תמונה:gmaker_if_collision_button.png כדי לבדוק אם הצורה מתנגשת במשהו (גבול או ערימה). לא לשכוח לסמן Relative כדי לבדוק התנגשות במקום שבו הצורה נמצאת.
  3. אם יש התנגשות, לבטל את הסיבוב: להוסיף פעולת הצבה תמונה:gmaker_set_var.png ובה להציב אל המשתנה image_index את הערך -1 ולסמן Relative.

תמונה:gmaker_test_save.png מה קורה עכשיו כשמנסים לסובב את הקו ליד הגבול?



 שאר הצורות

עכשיו נוכל להוסיף תמונות גם בשביל שאר הצורות.


עכשיו תורך:

להוסיף תמונות מסובבות לדמויות S ו-Z, כמו שעשינו לקו. (בדמויות האלה צריך להגדיל את הנייר ל-48 פיקסלים.)

תמונה:tetris_Z_shape.png תמונה:tetris_S_shape.png


את הצורות L, J ו-T צריך לסובב סיבוב שלם. אחרי שמגדילים את הנייר שלהן ל-48 פיקסלים, צריך לבחור שבהנפשת הסיבוב יהיו 4 תמונות ו-360 מעלות.


תמונה:gmaker_test_save.png עכשיו אפשר לסובב את כל הצורות.



 המשך העבודה

השלמנו כמעט את כל הרשימה:

  • אובייקט לצורת הריבוע
  • נפילה
  • תנועה ימינה ושמאלה כשלוחצים על החצים
  • להערם בסוף הנפילה
  • לתכנת את שאר הצורות
  • לבחור את צבע הערימה לפי צורה
  • לבחור צורה באופן אקראי
  • לסובב את הצורות כשלוחצים על חץ למעלה
  • להעלים שורות שלמות

מציאת שורות מלאות

כשנוצר מופע ערימה חדש, הוא עשוי למלא חלל ככה שתיווצר שורה מלאה במופעי ערימה. אם שורה מתמלאת במופעי ערימה, צריך להעלים אותה ולהעלות את הניקוד.

איך נדע אם השורה התמלאה? נצטרך לבדוק את זה בארוע היצירה של אוביקט הערימה תמונה:gmaker_create.png. בסוף רשימת הפעולות של הארוע הזה, נוסיף הערה "לבדוק אם השורה מלאה". מתחת להערה הזאת נוסיף את הפעולות שיבדקו אם השורה מלאה.

איזה פעולות נצטרך?
כדי לגלות אם השורה מלאה, נרצה ללכת ימינה משבצת-אחר-משבצת ולבדוק אם בה ערימה או רווח. אם נגיע לגבול ועדיין לא נמצא רווח, נלך שמאלה משבצת-אחר-משבצת ונחפש רווח. אם נגיע לגבול השמאלי בלי למצוא רווח, נדע שהשורה מלאה. אם נמצא בדרך רווח, נדע שהשורה לא מלאה ונוכל להפסיק לבדוק את שאר המשבצות.

תמונה:tetris_line_full_check.png

זה אומר שנצטרך שתי לולאות אחת אחרי השניה: לולאה אחת בשביל ללכת ימינה עד הגבול, ולולאה שניה בשביל ללכת שמאלה עד הגבול.

אבל כמה פעמים צריכה לחזור כל לולאה? אנחנו לא יודעים למצוא את x של הגבול מימין ומשמאל, ככה שאנחנו לא יכולים לחשב כמה משבצות צריך לבדוק מימין ומשמאל. אנחנו יכולים רק ללכת הצידה משבצת-אחר-משבצת ולראות אם כבר הגענו לגבול (באמצעות הפעולה לבדיקת התנגשות באובייקט תמונה:gmaker_if_collision_object.png).

לכן לא נוכל להשתמש בפעולת החזרה תמונה:gmaker_repeat_button.png. נצטרך לתכנת לולאה בדרך אחרת.

 לולאת while

בשפת התכנות GML יש כמה פקודות בשביל לתכנת לולאה. אחת מהן היא repeat: לולאת החזרה המוכרת לנו, שחוזרת מספר פעמים על פקודה או בלוק. אבל יש פקודות אחרות שיוצרות לולאות מסוגים אחרים.

אנחנו נשתמש בלולאה שימושית במיוחד: לולאת while. הלולאה הזו חוזרת על פקודה או בלוק שוב ושוב, כל עוד מתקיים תנאי מסוים. כשהתנאי מפסיק להתקיים, הלולאה נגמרת. איזה תנאי? כל תנאי שנרצה. למשל, אם נרצה להזיז את המופע ימינה משבצת-אחר-משבצת עד שיצא מהחדר, נכתוב:

while ( x < 640 )
{
    x = x + 16;
}

הסבר על הקוד:

  • while היא מילת מפתח שמסמנת פקודה ללולאה שחוזרת כל עוד מתקיים תנאי.
  • ( x < 640 ) הוא התנאי של הלולאה. הוא חייב להיות בתוך סוגריים. בתוך הסוגריים אפשר לכתוב כל ביטוי שרוצים.
  • סימני הצומדיים { ו-} מסמנים תחילת וסוף בלוק.
  • x = x + 16; היא פקודת הצבה, שמציבה במשתנה x את הביטוי x + 16. כלומר: מגדילה את x ב-16.

בעברית אפשר לנסח את הקוד הזה ככה: "כאשר x קטן מ-640, תגדיל את x כל פעם ב-16."

הקוד הזה טוב ויפה, אבל הוא לא עוצר את הלולאה בגבול. בשביל לעצור את הלולאה כשמגיעים לגבול נצטרך תנאי אחר, תנאי שיבדוק אם יש מופע גבול במקום מסוים.

 הפונקציה place_meeting

בשביל לבדוק אם הגענו לגבול בלי קוד, משתמשים בפעולה לבדיקת התנגשות באובייקט תמונה:gmaker_if_collision_object.png. בקוד, אפשר להשתמש בפונקציה שבודקת את זה: הפונקציה place_meeting( x, y, border ) מחזירה 1 ("כן") אם יש מופע גבול במקום (x, y). היא מחזירה 0 ("לא") אם אין שם מופע כזה. באנגלית place_meeting (פְּלֵייס מִיטִינְג) זה "מקום התקלות".

לכן נוכל לכתוב לולאה כזאת:

x_check = x + 16;
while ( !place_meeting( x_check, y, border ) )
{
    x_check = x_check + 16;
}

נעבור על הקוד לאט-לאט כדי להבין אותו:

  • x_check = x + 16; היא פעולת הצבה שמציבה למשתנה x_check את המיקום האופקי שנמצא משבצת אחת מימין למופע: x + 16.
  • while מסמנת לולאה. הלולאה תחזור כל עוד הביטוי בסוגריים שאחרי מילת המפתח while יהיה נכון.
  • הסימן ! מסמן להפוך את התוצאה של הביטוי, ממש כמו תיבת הסימון NOT שבפעולות הבדיקה. אפילו קוראים לו NOT. הוא גורם שבמקום שהלולאה תתבצע כאשר הביטוי נכון, היא תתבצע כל עוד הוא לא נכון.
  • place_meeting( x_check, y, border ) היא פונקציה שבודקת אם יש מופע גבול במקום (x_check, y). אם כן, היא תחזיר 1, הסימן ! יהפוך את זה ל-0, והלולאה תפסיק. אם אין שם גבול, הפונקציה תחזיר 0, הסימן ! יהפוך את זה ל-1, והבלוק שמתחת לפקודה while יתבצע. כלומר: הלולאה תחזור כל עוד אין מופע גבול במקום (x_check, y).
  • x_check = x_check + 16; היא פקודת הצבה שמגדילה את x_check ב-16. ככה בפעם הבאה שהלולאה תתבצע, נבדוק את המקום שנמצא משבצת אחת ימינה.

כלומר: הקוד הזה מקדם את x_check ימינה משבצת-אחר-משבצת, עד שמגיעים לגבול.

כתבנו את הלולאה שרצינו; עכשיו נוכל לשים בה את הפעולות שאנחנו רוצים לעשות: אנחנו רוצים למצוא רווח - מקום שבו אין מופע ערימה. אם נמצא מקום כזה, נוכל להפסיק את הבדיקה. הנה הקוד שעושה את זה:

if ( !place_meeting( x_check, y, pile ) )
    exit;

נעבור עליו:

  • if היא מילת מפתח שמסמנת פקודת בדיקה. הפקודה שאחריה תתבצע רק אם התנאי בסוגריים נכון.
  • הסימן ! מסמן NOT: להפוך את התוצאה של הביטוי שבודקים.
  • place_meeting( x_check, y, pile ) היא פונקציה שבודקת אם יש מופע ערימה במקום (x_check, y). אם כן, היא תחזיר 1, הסימן ! יהפוך את התוצאה ל-0, והפעולה הבאה לא תתבצע. אם אין שם ערימה, הפונקציה תחזיר 0, הסימן ! יהפוך את התוצאה ל-1, והפעולה הבאה תתבצע. כלומר: הפעולה הבאה תתבצע אם אין מופע ערימה במקום (x_check, y).
  • exit; היא מילת מפתח שמסמנת פקודת יציאה מהקוד, בדומה לפעולה ליציאה מהארוע תמונה:gmaker_exit.png. באנגלית המילה exit (אֵקְסִיט) משמעה "צא" או "יציאה".

כלומר, הקוד הזה אומר: "אם אין ערימה במקום (x_check, y) - תפסיק לבצע את הקוד."

אם נכניס את הקוד הזה לתוך הלולאה, נקבל את הקוד:

x_check = x + 16;
while ( !place_meeting( x_check, y, border ) )
{
    if ( !place_meeting( x_check, y, pile ) )
        exit;
    x_check = x_check + 16;
}

נבדוק אם זה עובד: נשים פעולת קוד תמונה:gmaker_code_button.png בסוף ארוע היצירה של הערימה תמונה:gmaker_create.png, ונעתיק אליה את הקוד מלמעלה. כדי לראות אם הלולאה הסתיימה או שיצאנו ממנה באמצע, צריך לעשות משהו אחרי סוף הבלוק. הכי פשוט יהיה להשמיע צליל, ואז כשנשמע את הצליל נדע שלולאה הסתיימה ולא יצאנו ממנה באמצע.

לכן ניצור משאב צליל חדש תמונה:gmaker_sound_button.png בשם full, ואז נוסיף בסוף הקוד את השורה: sound_play(full);. הפונקציה sound_play גורמת להשמעת צליל, ממש כמו הפעולה להשמעת צליל תמונה:gmaker_sound_button.png. הקוד המלא בפעולת הקוד יהיה:

x_check = x + 16;
while ( !place_meeting( x_check, y, border ) )
{
    if ( !place_meeting( x_check, y, pile ) )
        exit;
    x_check = x_check + 16;
}
sound_play(full);



תמונה:gmaker_test_save.png נסו להזיז צורה ימינה עד הגבול - כשהיא תנחת אמור להישמע צליל. נסו להנחית צורה באמצע או משמאל - כשהיא תנחת לא אמור להיות צליל.



 הצד השני

בדקנו אם השורה מלאה מימין למופע ערימה. נוכל לבדוק באופן דומה את צד שמאל. הקוד לבדיקת הצד השמאלי של השורה דומה מאוד לקוד שהשתמשנו בו קודם. ההבדלים היחידים הם בפעולות ההצבה: במקום לזוז ימינה באמצעות הוספת 16, זזים שמאלה באמצעות הורדת 16.

x_check = x - 16;
while ( !place_meeting( x_check, y, border ) )
{
    if ( !place_meeting( x_check, y, pile ) )
        exit;
    x_check = x_check - 16;
}

את הקוד הזה צריך להוסיף לפעולת הקוד שהשתמשנו בה קודם (לפני הפקודה שמשמיעה צליל).


תמונה:gmaker_test_save.png עכשיו הצליל אמור להישמע רק כאשר מתמלאת שורה שלמה.




{{{גודל}}}

כדאי לדעת:

מה קורה כשהצורה שנחתה יוצרת כמה מופעי ערימה באותה השורה?

כאשר מופע הערימה הראשון יתחיל לבדוק אם השורה מלאה, הוא יגלה שהמשבצת שלידו ריקה (כי עדיין לא נוצר שם מופע ערימה) ויפסיק את הבדיקה. רק כשהמופע האחרון ייווצר, הוא ימצא את כל המופעים שנוצרו לידו. לכן רק המופע האחרון ישלים את השורה וישמיע את הצליל.




{{{גודל}}}

כדאי לדעת:

איך מגלים איזה פונקציות ופקודות אפשר לכתוב בקוד?

מי שיודע אנגלית, יכול למצוא את כל הפונקציות והפקודות בעזרה של Game Maker, בחלק שנקרא The Game Maker Language.

שורה מלאה

כששורה מתמלאת, היא צריכה להבהב כמה פעמים, ואז כל מופעי הערימה שבה צריכים להימחק וכל המופעים שמעליהם צריכים לרדת שורה.

איך נתכנת את ההתנהגות הזאת? יש שתי אפשרויות:

  1. לתכנת אותה באובייקט הערימה.
  2. לתכנת אותה באובייקט חדש שיהיה אחראי לשורות מלאות.

שתי האפשרויות טובות ומתאימות. לטעמי, האפשרות השניה (ליצור אובייקט חדש לשורה מלאה) היא ברורה ונקיה יותר, ולכן בחרתי בה. אבל אפשר לבחור גם אחרת.

אז ניצור אובייקט חדש בשם row. ניתן לו בהתחלה את הדמות של הגבול, רק בשביל לבדוק אותו - אחר כך נשנה את זה. אנחנו צריכים ליצור מופע של אובייקט השורה כל פעם שהערימה ממלאת שורה שלמה. זה קורה בפעולת הקוד תמונה:gmaker_code_button.png שבסוף ארוע היצירה של הערימה תמונה:gmaker_create.png. נוסיף עוד פקודה בסוף הקוד, אחרי הפקודה שמשמיעה צליל:

instance_create( x, y, row );

הפונקציה instance_create זהה לפעולה ליצירת מופע תמונה:gmaker_create_instance.png. הקוד שכתבנו יוצר מופע של אובייקט row במיקום הנוכחי של הערימה (x,y).


תמונה:gmaker_test_save.png כל פעם שמשלימים שורה אמור להופיע ריבוע על מופע הערימה שהשלים את השורה.



הצלחנו ליצור מופע שורה כל פעם שמתמלאת שורה בערימה, אבל המופע שיצרנו לא נראה כמו שורה אלא רק כמו ריבוע.

 גודל השורה

השורה צריכה להתחיל במופע הגבול השמאלי ולהימשך עד לימני. איך נדע איפה בדיוק ליצור אותה וכמה רחבה היא צריכה להיות?

אנחנו כבר יודעים איפה הגבול השמאלי: ב-x_check. הרי הלולאה השניה נגמרה בדיוק כשהגענו לגבול השמאלי, לכן אחריה x_check עדיין אומר איפה הגבול השמאלי. לכן נרצה ש-x של השורה יהיה x_check + 16 כדי שהיא תוצב בדיוק משבצת אחת ימינה מהגבול. בשביל זה נשנה את השורה האחרונה של הקוד לשורה הזאת:

instance_create( x_check+16, y, row );

תמונה:gmaker_test_save.png עכשיו הריבוע אמור להופיע בצד שמאל של השורה שמשלימים.



מצאנו את המקום המתאים לשורה, עכשיו צריך למצוא את הגודל. בשביל זה צריך לדעת איפה הגבול הימני.


{{{גודל}}}

כדאי לדעת:

למה לא לתת לשורה גודל קבוע, לפי הגודל שציירנו את הגבול בחדר?

גודל קבוע יקשה לשנות בעתיד את רוחב אזור המשחק. אם השורה תהיה בגודל קבוע אז לא נוכל ליצור חדרים שונים שבכל אחד מהם רוחב שונה. וגם לא נוכל ליצור אזור משחק לא מלבני (למשל בצורת משולש הפוך).

בקיצור: גודל שורה קבוע מראש יגביל את היכולת שלנו לשנות ולשפר את המשחק.



אנחנו יכולים לדעת איפה הגבול הימני אחרי הלולאה הראשונה, כי אז x_check אומר איפה הגבול הימני. לכן נוסיף אחרי הבלוק של הלולאה הראשונה את השורה הזאת:

x_right = x_check;

עכשיו x_right אומר איפה הגבול הימני. לכן הרוחב של השורה צריך להיות x_right - x_check - 16. למה פחות 16? כי x_check מראה את הקצה השמאלי של הגבול השמאלי, ואנחנו רוצים שהשורה תתחיל רק בקצה הימני שלו, כלומר שהיא תהיה קצרה יותר ב-16 פיקסלים.

איך נשנה את רוחב השורה?
לכל מופע יש משתנה בשם image_xscale שאומר פי כמה להגדיל את הדמות לרוחב. בדרך כלל הערך שלו הוא 1 ואז הדמות מופיעה בגודל הרגיל. אבל אם משנים אותו ל-2 היא תוצג ברוחב כפול. ואם נשנה אותו ל-10 אז היא תוצג ברוחב פי 10 מהרגיל.

הדמות של השורה היא ברוחב 16 פיקסלים, לכן צריך להרחיב אותה פי (x_right - x_check - 16) / 16. כלומר, צריך להוסיף בסוף הקוד את השורה:

image_xscale = (x_right - x_check - 16) / 16;



תמונה:gmaker_test_save.png האם השורה בגודל המתאים?



לא שינינו את גודל השורה אלא את גודל מופע הערימה, כי המשתנה image_xscale שהצבנו אליו שייך למופע הערימה שבו מבוצע הקוד ולא למופע השורה שיצרנו בשורה הקודמת.

אז אולי במקום לכתוב image_xscale נכתוב row.image_xscale כמו שעשינו במשחקים הקודמים? הבעיה עם זה היא שכאשר יש יותר ממופע אחד של row, הצבה למשתנה row.image_xscale תשנה את image_xscale בכל המופעים של row. אנחנו רוצים לשנות רק את השורה שיצרנו, כי יכול להיות שיש שורות אחרות ברוחב אחר (אם בוחרים לצייר אזור משחק לא מלבני ומשלימים כמה שורות בבת אחת). לכן אנחנו צריכים למצוא דרך לכתוב image_xscale של המופע המסוים שיצרנו.

 זיהוי מופעים

לכל מופע במשחק יש מספר זיהוי. אפשר לראות את המספר הזה בחלון תכונות החדר: מצביעים על מופע עם העכבר, ואז אפשר לראות את מספר הזיהוי שלו למטה בשורת המצב.

תמונה:gmaker_instance_id.png

מספר הזיהוי הזה הוא כמו שם פרטי של מופע, ואפשר להשתמש בו כמו שמשתמשים בשם האובייקט. למשל, אפשר לכתוב:

id = 100063;
id.image_xscale = 7;

ואז ישתנה הרוחב של מופע 100063 בלבד.

לכן אנחנו צריכים את מספר הזיהוי של מופע השורה שיצרנו. למרבה השמחה, הפונקציה instance_create מחזיר האת מספר הזיהוי של המופע שהיא יוצרת. לכן נשנה את שתי השורות האחרונות של הקוד לזה:

r = instance_create( x_check+16, y, row );
r.image_xscale = (x_right - x_check - 16) / 16;

זה אומר שמציבים במשתנה r את הערך שמחזירה הפונקציה instance_create ואז r מזהה את המופע החדש שנוצר, ולכן r.image_xscale מסמן את המשתנה image_xscale של המופע החדש שנוצר.


תמונה:gmaker_test_save.png עכשיו דמות השורה מכסה את כל השורה שהושלמה.



 הבהוב

במשחק פקמן ראינו כבר שהבהוב הוא פשוט סוג של הנפשה. לכן צריך ליצור דמות חדשה בשביל אובייקט השורה, ולערוך אותה ככה שהיא תיראה מהבהבת.


עכשיו תורך:

ליצור לאובייקט השורה דמות חדשה בגודל 16 פיקסלים, ולגרום לה להבהב כמו שעשינו במשחק פקמן, אבל יותר מהר.



השורה צריכה להבהב כמה פעמים ואז להעלם. איך נספור את ההבהובים? אפשרות אחת היא באמצעות שעון מעורר. אבל יש עוד אפשרות נוחה: כל פעם שדמות גומרת להציג את התמונה האחרונה ברצף ההנפשה וחוזרת לתמונה הראשונה, קורה ארוע סוף ההנפשה (Animation end) תמונה:gmaker_other.png. נוסיף את הארוע הזה לאובייקט השורה ונשים בו את הפעולה למחיקת אובייקט תמונה:gmaker_destroy_button.png כדי למחוק את השורה.



תמונה:gmaker_test_save.png השורה מהבהבת פעם אחת ודי.



כדי שהשורה תהבהב כמה פעמים לפני שהיא נמחקת, נוסיף עוד תמונות לדמות המונפשת שלה:

  1. נלך לחלון עריכת הדמות.
  2. נקליק על אחת התמונות ונקיש Control+C כדי להעתיק אותה.
  3. נקיש Control+V כדי להדביק עותק שלה.
  4. נעשה אותו הדבר לתמונה השניה.
  5. נשנה את סדר התמונות ככה שהצבע יתחלף בין תמונה לתמונה כדי שיהיה הבהוב.
  6. נעתיק ונדביק שוב כדי שבסך הכל ההנפשה תכלול שלושה הבהובים.



תמונה:gmaker_test_save.png השורה מהבהבת שלוש פעמים.

העלמת השורה

אחרי שהשורה מהבהבת, היא צריכה להעלם: למחוק את כל מופעי הערימה שבשורה, ולהוריד משבצת אחת למטה את כל מופעי הערימה שמעליה.

בשביל זה צריך לעבור על כל מופעי הערימה ולבדוק את הגובה של כל אחד מהם: אם הוא גבוה מהשורה צריך להנמיך אותו במשבצת, ואם הוא בגובה השורה בדיוק אז צריך למחוק אותו. כלומר, צריך לולאה שעוברת על כל מופעי הערימה. בשפת GML יש לולאה מתאימה.

 לולאת with

כשרוצים לחזור על פקודות מסויימות עבור כל המופעים של אובייקט מסוים, משתמשים בלולאת with. למשל:

with (pile)
{
    y = y + 16;
}

מה שזה אומר:

  • with היא מילת מפתח שמסמנת פקודה ללולאה שחוזרת עבור כל אחד מהמופעים של אובייקט מסוים.
  • הביטוי בסוגריים (pile) שאחרי המלה with מסמן את האובייקט שעל המופעים שלו הלולאה תעבור.
  • סימני הצומדיים { ו-} מסמנים תחילת וסוף בלוק.
  • y = y + 16; היא פקודת הצבה, שמגדילה ב-16 את y של המופע שעליו חוזרת הלולאה. כלומר, כל פעם מדובר ב-y של מופע ערימה אחר. זה לא y של המופע שבו מבצעים את פעולת הקוד.

בעברית אפשר לנסח את הקוד הזה ככה: "עם כל אחד מהמופעים של אובייקט הערימה, תגדיל את y ב-16." בואו נראה איך זה עובד: נוסיף פעולת קוד תמונה:gmaker_code_button.png עם הקוד הזה, לארוע סוף ההנפשה של השורה תמונה:gmaker_other.png.


תמונה:gmaker_test_save.png מה קורה כשמתמלאת שורה שלמה?



 משתנים בתוך לולאת with

הורדנו את כל מופעי הערימה, אבל צריך להוריד רק את אלה שמעל השורה. כלומר, צריך להכניס פקודת if (אם-אז) לפני פקודת ההצבה, כדי שההצבה תתבצע רק אם המופע בלולאת with גבוה מהשורה.

איך כותבים את זה בקוד?
הגובה של מופע הערימה שבלולאה הוא y. צריך לבדוק שהוא קטן יותר מ-y של השורה (כי אז זה אומר שמופע הערימה גבוה יותר מהשורה). אבל אם כותבים y בתוך לולאת with זה אומר y של מופע הערימה שעליו חוזרים, אז איך נכתוב y של השורה שבה מתבצעת פעולת הקוד?

בתוך לולאת with, אפשר לכתוב other.y כדי לקבל את y של המופע שבו מתבצעת פעולת הקוד. לכן במקרה שלנו, other.y יתן לנו את y של השורה. לכן נשנה את הקוד:

with (pile)
{
    if ( y < other.y )
        y = y + 16;
}

עכשיו פקודת ההצבה תתבצע רק למופעי הערימה שגבוהים מהשורה.


תמונה:gmaker_test_save.png נסו למלא שורה שלמה ולראות מה קורה עכשיו.



הורדנו את כל מופעי הערימה שמעל השורה, אבל צריך גם למחוק את המופעים שבאותו גובה כמו השורה.

 מחיקת מופע בקוד

כמו שיש פעולה למחיקת מופע תמונה:gmaker_destroy_button.png, יש גם פונקציה שמוחקת מופע: instance_destroy(). הפונקציה instance_destroy פשוט מוחקת את המופע הנוכחי. בתוך לולאת with היא מוחקת את המופע שהלולאה עוברת עליו כרגע.

אנחנו רוצים למחוק רק מופעי ערימה שבאותו גובה כמו השורה, לכן נוסיף בתוך הלולאה את הקוד המודגש:

with (pile)
{
    if ( y == other.y )
        instance_destroy();
    if ( y < other.y )
        y = y + 16;
}

הקוד המודגש אומר: אם y של המופע שהלולאה עוברת עליו כרגע שווה ל-y של השורה, אז תמחק את המופע הזה.


תמונה:gmaker_test_save.png עכשיו השורה תעלים את כל מופעי הערימה שבה.



 המשך העבודה

השלמנו את כל רשימת העבודה. המשחק עובד ואפשר לשחק בו. מה שנשאר זה להציג לוח ניקוד יפה, רקע מתאים, ואריזה גרפית כמו שעשינו במשחק חייזרים. כדאי גם לעשות את המשחק מאתגר יותר: להאיץ את מהירות הנפילה של הצורות, כל פעם קצת.

לכן רשימת העבודה החדשה תהיה:

  • לוח ניקוד
  • האצת מהירות הנפילה
  • רקע
  • אריזה

לוח הניקוד

ברוב הגירסאות של טטריס לוח הניקוד מציג שלושה דברים:

  1. הניקוד
  2. מספר השורות שהושלמו
  3. הרמה

גם אנחנו ניצור לוח ניקוד שמציג את שלושת הדברים האלה.


עכשיו תורך:

ליצור אובייקט score_panel תמונה:gmaker_score_panel.png שיציג את הניקוד, ולהוסיף מופע שלו לחדר. לתת לו להציג מתחת לניקוד עוד שני משתנים גלובליים: אחד בשם lines והשני בשם level. לאתחל את שני המשתנים האלה ל-0 בארוע Game start תמונה:gmaker_other.png.



את הניקוד ואת מספר השורות צריך להגדיל בכל פעם שנעלמת שורה. לכן בארוע סוף ההנפשה תמונה:gmaker_other.png של השורה צריך להוסיף את הפעולה לקביעת הניקוד תמונה:gmaker_set_score.png ואת פעולת ההצבה תמונה:gmaker_set_var.png כדי להגדיל את הניקוד ואת מספר השורות global.lines.


תמונה:gmaker_test_save.png הניקוד ומספר השורות צריכים לעלות כשמשלימים שורות.



גם כשמנחיתים צורה הניקוד צריך לעלות בהתאם לגובה ההנחתה. לכן נלך לארוע הקשה על מקש רווח תמונה:gmaker_key_down.png של אובייקט הצורה, ונוסיף בסופו את הפעולה לקביעת הניקוד תמונה:gmaker_set_score.png. כמה ניקוד נוסיף? לפי גובה הנחיתה. הגובה הזה הוא ההפרש בין y לבין yprevious. לכן נכתוב בחלון אפשרויות הפעולה (y - yprevious) / 16 וכמובן נסמן Relative.


תמונה:gmaker_test_save.png הניקוד צריך לעלות גם כשמנחיתים צורה.



בהזדמנות זו, נוכל לעשות עוד משהו בארוע ההקשה על רווח: כרגע ההנחתה מכוונת את השעון המעורר, ואז ארוע השעון המעורר מפעיל את ארוע המשתמש, ושם הצורה הופכת לערימה. במקום זה, אפשר להפעיל את ארוע המשתמש ישירות בהנחתה. בשביל זה צריך:

  1. תמונה:gmaker_clock_button.png למחוק את הפעולה לכיוון שעון מעורר.
  2. תמונה:gmaker_code_button.png במקומה לשים פעולת קוד, שבה הפקודה event_user(0);.

תמונה:gmaker_test_save.png המשחק אמור להתנהג אותו הדבר כמו קודם, כי עשינו רק refactoring.



 הרמה

אחרי כל עשר שורות שמשלימים, עולה הרמה. ככל שהרמה גבוהה יותר מקבלים יותר נקודות וגם המשחק נעשה יותר מהיר. זה אומר שנצטרך לעשות שלושה דברים:

  • להעלות רמה כל עשר שורות.
  • לשנות את הפעולות לקביעת ניקוד ככה שיתנו ניקוד גבוה יותר לפי הרמה.
  • לשנות את מהירות נפילת הצורה לפי הרמה.

נתחיל בהעלאת הרמה: נלך להעלמת שורה בארוע סוף ההנפשה תמונה:gmaker_other.png של השורה. בסוף רשימת הפעולות שלו צריך להוסיף פעולת בדיקה כדי לבדוק אם מספר השורות שהושלמו מתחלק ב-10 (למשל: 10, 20, 30, או אפילו 100) כי אז צריך לעלות רמה. איזה פעולת בדיקה תוכל לבדוק את זה? הפעולה לבדיקת ערך של משתנה תמונה:gmaker_if_var.png לא תתאים, כי אנחנו לא רוצים לבדוק רק ערך אחד אלא סוג שלם של ערכים: כל הערכים שמתחלקים ב-10. לכן נשתמש בפעולת בדיקה אחרת בלשונית control: הפעולה לבדיקת ביטוי תמונה:gmaker_if_expr.png.

הפעולה לבדיקת ביטוי מאפשרת לבדוק ביטוי שרוצים (ממש כמו פקודת if בקוד). אנחנו צריכים ביטוי שאומר אם מספר מתחלק ב-10. אין ב-GML פונקציה שבודקת את זה, אבל יש אפשרות לחשב את השארית שנשארת כשמחלקים מספר ב-10, באמצעות פעולת החשבון mod. הביטוי 27 mod 10 ייתן את השארית שנשארת כשמחלקים 27 ב-10, כלומר: 7.

לכן בחלון אפשרויות הפעולה לבדיקת ביטוי נכתוב global.lines mod 10 == 0 כי הביטוי הזה יהיה נכון רק אם מספר השורות מתחלק ב-10 בדיוק (השארית שווה ל-0).

אם הבדיקה מצליחה, צריך להעלות את הרמה: תמונה:gmaker_set_var.png להגדיל את המשתנה global.level ב-1.


תמונה:gmaker_test_save.png נסו להשלים שורות ולראות אם הרמה בלוח הניקוד עולה בהתאם.



 קודים לבדיקות

לוקח הרבה זמן לבדוק שהרמה באמת מתקדמת כל 10 שורות. ובהמשך נצטרך לעלות רמות שוב ושוב כדי לבדוק את האצת קצב הנפילה ואת חישוב הניקוד ברמות גבוהות. היה נוח יותר אילו יכולנו להגדיל את מספר השורות או את הרמה במהלך המשחק כדי לבדוק את הדברים האלה. הכי קל לעשות את זה באמצעות קודים, כמו שעשינו במשחק פקמן: להוסיף ארוע הקשה על מקש שיגרום להגדלת מספר השורות.

בשביל לעשות את זה נוסיף לאובייקט לוח הניקוד ארוע הקשה על איזשהו מקש תמונה:gmaker_key_down.png, למשל Control (אפשר לבחור משהו אחר אם רוצים). בארוע הזה נוסיף פעולת הצבה תמונה:gmaker_set_var.png ובה נגדיל את global.lines ב-1. ככה נוכל להגדיל את מספר השורות ל-9 או 19 או 29 במהירות, ואז לבדוק בקלות את עליית הרמה.


תמונה:gmaker_test_save.png עכשיו אפשר לבדוק גם עליה לרמות גבוהות.



 ניקוד לפי הרמה

הניקוד על השלמת שורה והנחתת צורה צריך להיות תלוי ברמה: ברמה 0 מקבלים 40 נקודות לשורה, ברמה 1 מקבלים 80 נקודות לשורה, ברמה 2 מקבלים 120 נקודות לשורה, וכן הלאה. במלים אחרות, כל פעם שמשלימים שורה (בארוע סוף ההנפשה של השורה תמונה:gmaker_other.png) צריך להוסיף לניקוד תמונה:gmaker_set_score.png 40 * (global.level + 1).

גם את הניקוד על הנחתת צורה צריך להכפיל ב-(global.level + 1). כלומר, בארוע הקשה על רווח של הצורה תמונה:gmaker_key_down.png צריך לשנות את פעולת קביעת הניקוד תמונה:gmaker_set_score.png כך שתוסיף (y - yprevious) / 16 * (global.level + 1) נקודות.


תמונה:gmaker_test_save.png ככל שהרמה יותר גבוהה, השלמת שורות והנחתת צורות תתן יותר נקודות.



 מהירות הנפילה

כל פעם שעולים רמה, הצורה צריכה ליפול קצת יותר מהר. זה אומר שבמקום לקבוע שעון מעורר לעוד 20 צעדים כדי ליפול משבצת, הצורה צריכה משתנה שיגיד כמה צעדים לחכות עד לנפילה הבאה. כשהרמה עולה, נקטין את הערך של המשתנה הזה.

בשביל זה צריך:

  1. תמונה:gmaker_create.png ללכת לארוע היצירה של הצורה.
  2. תמונה:gmaker_set_var.png לשים בתחילתו פעולת הצבה, ובה לאתחל ל-20 משתנה בשם delay.
  3. תמונה:gmaker_clock_button.png לשנות את פעולת כיוון השעון המעורר ככה שהשעון יצלצל עוד delay צעדים (במקום 20).
  4. תמונה:gmaker_alarm.png ללכת לארוע השעון המעורר שבו מתבצעת הנפילה של הצורה.
  5. תמונה:gmaker_clock_button.png גם כאן לשנות את פעולת כיוון השעון המעורר ככה שהשעון יצלצל עוד delay צעדים במקום 20.

תמונה:gmaker_test_save.png המשחק אמור להתנהג כמו קודם, כי בינתיים רק עשינו refactoring בלי לשנות התנהגות.



עכשיו צריך להאיץ את קצב הנפילה בכל פעם שעולים רמה:

  1. תמונה:gmaker_other.png ללכת לארוע סוף ההנפשה של אובייקט השורה.
  2. תמונה:gmaker_if_expr.png הפעולה לבדיקת ביטוי אומרת מתי עולים רמה, לכן צריך להוסיף אחריה את הפעולות להתחלת וסוף בלוק. (לשמור שפעולת ההצבה תמונה:gmaker_set_var.png שמעלה את הרמה תהיה בתוך הבלוק הזה.)
  3. תמונה:gmaker_set_var.png להוסיף לבלוק עוד פעולת הצבה, ובה להקטין ב-1 את המשתנה delay של הצורה: shape.delay.

תמונה:gmaker_test_save.png עכשיו המשחק אמור להיות קצת יותר מהיר כל פעם שעולים רמה.



מה קורה אם מגיעים לרמה 20? המשתנה delay יהיה 0! לכן לפני שמקטינים אותו צריך להכניס פעולה לבדיקת ערך של משתנה תמונה:gmaker_if_var.png ובה לוודא שהוא גדול מ-2 או אולי אפילו 3.


תמונה:gmaker_test_save.png עכשיו המשחק לא ייתקע כשמגיעים לרמות גבוהות.



 המשך העבודה

בואו נראה איפה אנחנו ברשימת העבודה:

  • לוח ניקוד
  • האצת מהירות הנפילה
  • רקע
  • אריזה

עיצוב החדר

כרגע יש לנו חדר לא מאוד יפה, אבל עם משחק עובד. בואו נעצב את החדר כדי שייראה יותר יפה.

קודם כל, החדר רחב מדי - במשחק לא משתמשים בכל הרוחב. לכן כדאי לשנות את הרוחב שלו:

  1. ללכת לחלון תכונות החדר.
  2. לעבור ללשונית settings.
  3. לשנות את הערך שכתוב בשדה Width כדי שהחדר לא יהיה רחב מדי.
  4. אם החדר נראה לך גבוה מדי, אפשר לשנות גם את הערך שכתוב בשדה Height.

עכשיו נוסיף לו רקע תמונה:gmaker_add_bg.png. אפשר לבחור איזו תמונה שרוצים - תמונה שצילמת או תמונה מפליקר. כדאי רק לדאוג להתאים את הגודל שלה לגודל החדר.


תמונה:gmaker_test_save.png איך המשחק נראה עכשיו?



ברוב התמונות, קצת קשה לראות את לוח הניקוד, וגם אזור המשחק והצורה הבאה פחות ברורים כשיש רקע. לכן כדאי לצייר על הרקע מלבנים בצבע לבן באזורים שעליהם יוצגו אזור המשחק, הצורה הבאה, ולוח הניקוד.


תמונה:gmaker_test_save.png איך המשחק נראה עכשיו?



אובייקט הגבול לא מוסיף ליופי של החדר. עכשיו גם ממילא אפשר לדעת איפה גבול אזור המשחק, לפי הרקע. לכן כדאי להפוך את הגבול לאובייקט נסתר: להוריד לו את הסימון מתיבת הסימון Visible.


תמונה:gmaker_test_save.png יותר יפה, לא?



 מוזיקת רקע

טטריס הוא משחק בלי פיצוצים או כל מיני אפקטים מיוחדים. לכן נהוג לשים בו מוזיקת רקע, כדי להעסיק גם את חוש השמיעה בזמן המשחק. השמעת מוזיקת רקע ב-Game Maker דומה להשמעת צליל רגיל: מוסיפים משאב צליל ומשמיעים אותו בפעולה להשמעת צליל תמונה:gmaker_sound_button.png. יש רק שני הבדלים:

  1. סוג הקובץ של הצליל הוא שונה: במקום קובץ מסוג wav צריך להשתמש בקובץ מסוג mid. אפשר למצוא הרבה קבצי mid חופשיים לשימוש באתרים כמו פרוייקט מוטופיה או מוזיקה ברשות הכלל. כדאי לחפש שם משהו שימצא חן בעיניך.
  2. בחלון אפשרויות הפעולה של פעולת השמעת הצליל, לשנות את השדה loop ל-true כדי שאחרי שמוזיקה נגמרת היא תתחיל שוב מהתחלה.

את פעולת הצליל כדאי לשים בארוע תחילת המשחק של לוח הניקוד.


תמונה:gmaker_test_save.png המוזיקה אמורה להתנגן ברקע בזמן המשחק.



יכול להיות שחלק מהמשתמשים לא ירצו לשמוע מוזיקה במהלך המשחק. כדאי לתת להם אפשרות להפסיק אותה:

  1. תמונה:gmaker_key_down.png להוסיף ללוח הניקוד ארוע הקשה על מקש כלשהו, למשל M.
  2. תמונה:gmaker_if_sound.png לשים בו פעולה לבדיקה אם מתנגן צליל (שנמצאת בלשונית main1). בחלון אפשרויות הפעולה לבחור את הצליל של מוזיקת הרקע שבחרת.
  3. תמונה:gmaker_stop_sound.png אם המוזיקה מתנגנת, צריך להפסיק אותה באמצעות הפעולה להפסקת צליל (שגם היא נמצאת בלשונית main1).
  4. תמונה:gmaker_else.png להוסיף את פעולת בדיקת ההפך ("אחרת").
  5. תמונה:gmaker_sound_button.png אם לא מתנגנת מוזיקה, צריך להפעיל אותה באמצעות הפעולה להשמעת צליל.

תמונה:gmaker_test_save.png עכשיו אפשר להפסיק את המוזיקה או להשמיע אותה שוב באמצעות לחיצה על מקש M.



 אריזה

עכשיו תורך:

להוסיף למשחק הוראות שימוש, תמונת פתיחה ואייקון כמו שעשינו במשחק חייזרים.



עכשיו אפשר ליצור תוכנה עצמאית ולשלוח לחברים!


 קרידיט וויקיספר


  הודעות אפשר גם לשלוח לאיימל ioman1@walla.com