תכנות לילדים באמצעות Game Maker/טטריס/צעד 1: צורה
מתוך ויקיספר, אוסף ספרי הלימוד והמדריכים החופשי.
הצורות
בטטריס יש מעט מאוד דברים. הדבר העיקרי שיש במשחק זה צורות שנופלות. יש שבע צורות כאלה:
|
עכשיו תורך:
לשמור את הצורות אצלך במחשב, וליצור להן דמויות במשחק. (עדיין לא ליצור אובייקטים!) כדאי לתת לדמויות שמות משמעותיים; נהוג להשתמש בשמות האותיות באנגלית שצורתן דומה לצורות של טטריס: O, I, T, L, J, Z, S. |
מה הצורות האלה עושות?
- נופלות למטה בקפיצות.
- זזות כשלוחצים על החצים ימינה ושמאלה.
- מסתובבות כשלוחצים על חץ למעלה.
- נערמות אחת על השניה אחרי שהן נופלות.
- נעלמות אם יש בערימה שורה שלמה.
אז זו רשימת העבודה שלנו כרגע.
במקום ליצור מיד 7 אובייקטים כדי שלכל צורה יהיה אובייקט, נתחיל מאובייקט אחד לצורה אחת. ככה לא נצטרך לשנות ולתקן 7 אובייקטים כל פעם, אלא רק אחד. אחרי שנשלים את הצורה הראשונה, יהיה ברור איך לתכנת את האחרות. לכן נוסיף לסוף רשימת העבודה עוד סעיף:
מאיזו צורה נתחיל? בדרך כלל עדיף להתחיל במה שנראה הכי פשוט, כדי שקודם כל נצליח ליצור משהו פשוט שעובד; אחר כך נוכל להוסיף לו דברים יותר מורכבים. הצורה הכי פשוטה מבין שבע הצורות היא הריבוע, כי הוא לא מסתובב. לכן רשימת העבודה המעודכנת היא:
- אובייקט לצורת הריבוע.
- נפילה.
- תנועה ימינה ושמאלה כשלוחצים על החצים.
- להערם בסוף הנפילה.
- להעלים שורות שלמות.
- לתכנת את שאר הצורות.
- לסובב את הצורות כשלוחצים על חץ למעלה.
אובייקט צורה נופלת
קודם כל ניצור אובייקט בשם shape (שֵייפּ) - צורה. ניתן לאובייקט הצורה את דמות הריבוע. ניצור חדר ונשים בו את אובייקט הצורה.
כדאי לוודא שרואים את צורת ההריבוע.
עכשיו צריך לגרום לצורה ליפול. עד עכשיו גרמנו לאובייקטים לזוז באמצעות קביעת המהירות שלהם. אבל בטטריס הצורות לא זזות במהירות קבועה, אלא יורדות מדי פעם בקפיצה. לכן הפעם נטפל בתנועה של הצורות בצורה שונה: נשנה את הקורדינטות של הצורה בכל פעם שהיא צריכה לזוז. איך נדע מתי היא צריכה לזוז? נכוון שעון מעורר:
- נוסיף לאובייקט הצורה ארוע יצירה.
- בארוע זה נשים פעולת כיוון שעון מעורר. נכוון את השעון ל-20 צעדים.
- נוסיף ארוע צלצול שעון.
- בארוע הזה נשנה את הערך של המשתנה y כדי להזיז את הצורה למטה: נוסיף לו 16 (לסמן Relative).
- נוסיף גם לארוע הזה פעולת כיוון שעון מעורר ושוב נכוון את אותו השעון ל-20 צעדים. ככה הצורה תרד שוב ושוב כל 20 צעדים.
עכשיו הצורה אמורה ליפול לאיטה, כל פעם בקפיצה של 16 פיקסלים.
ברוב הגירסאות של טטריס, לחיצה על חץ למטה גורמת לצורה ליפול במהירות גדולה יותר. אצלנו השעון המעורר קובע את מהירות הנפילה, לכן כשלוחצים על חץ למטה צריך לכוון את השעון למספר צעדים קטן:
- להוסיף ארוע לחיצה על חץ למטה (Down).
- בארוע הזה לכוון את השעון המעורר ל-1 צעדים.
נסו ללחוץ על חץ למטה.
תנועה לצדדים
גם את התנועה לצדדים נעשה באמצעות שינוי הקורדינטות:
- נוסיף ארוע לחיצה על חץ ימינה (Right).
- בארוע הזה נשנה את המשתנה x כדי להזיז את הצורה ימינה: נוסיף לו 16 (לסמן Relative). ככה הצורה תזוז ימינה 16 פיקסלים.
- נעשה אותו הדבר לתנועה שמאלה (Left), רק שהפעם נוסיף ל-x את הערך -16 כדי להזיז את הצורה שמאלה.
נסו להזיז את הצורה ימינה ושמאלה בעזרת החצים.
הצורה זזה כשלוחצים על החצים, אבל היא זזה מהר מדי ואז קשה לכוון איפה היא תהיה בדיוק. בטטריס חשובה גם תנועה מהירה (בעיקר כשהמשחק מתקדם ונהיה מהיר) אבל גם הדיוק חשוב, כדי שיהיה אפשר להפיל את הצורות בדיוק למקום הנכון. הדרך הנהוגה להשיג גם דיוק וגם מהירות היא שבהתחלת הלחיצה הצורה תזוז רק פעם אחת, ואם המשתמש ממשיך ללחוץ אז הצורה זזה מהר. כלומר: צריך להפריד בין התזוזה הראשונה לתזוזות הבאות, ככה שתהיה השהיה אחרי התזוזה הראשונה.
הפרדת התזוזה הראשונה
גם במשחק חייזרים היינו צריכים להפריד בין דברים שקרו מהר מדי: שם הפרדנו בין היריות בעזרת משתנה מצב. גם הפעם נשתמש במשתנה מצב כדי להפריד בין התזוזות. אבל הפעם למשתנה שלנו יש 3 מצבים:
- לפני התזוזה הראשונה - אפשר לזוז.
- אחרי התזוזה הראשונה - לא לזוז עד שנגמרת ההשהיה.
- אחרי ההשהיה - שוב אפשר לזוז.
נקרא למשתנה המצב שלנו move_state. הוא יוכל לקבל אחד משלושה ערכים:
- לפני התזוזה הראשונה הערך שלו יהיה 0.
- בזמן ההשהיה שאחרי התזוזה הראשונה הערך יהיה 1.
- אחרי שנגמרה ההשהיה הערך יהיה 2.
קודם כל נאתחל את המשתנה: נוסיף לארוע היצירה את הפעולה לקביעת ערך של משתנה ושם נקבע את הערך של המשתנה move_state ל-0.
גם כשמפסיקים ללחוץ על החץ, צריך להחזיר את המשתנה move_state לערך 0, כדי שהוא יהיה מאותחל בפעם הבאה שהמשתמש ילחץ על חץ. לכן נוסיף שני ארועי שחרור מקש - אחד עבור חץ ימינה (Right) ואחד עבור חץ שמאלה (Left). בארועים האלה נשים את הפעולה לקביעת ערך של משתנה ונקבע את הערך של המשתנה move_state ל-0.
עכשיו נעבור לתזוזה עצמה: ארוע לחיצה על חץ . מה צריך לעשות בארוע הזה?
- במצב 2 (תזוזה מהירה) צריך פשוט לזוז. כלומר: לשנות את הערך של x.
- במצב 1 (המתנה) צריך לחכות בלי לזוז. כלומר: לא לעשות כלום.
- במצב 0 (תזוזה ראשונה) צריך לזוז, לעבור למצב 1, ולכוון שעון מעורר שיצלצל בסוף ההמתנה.
נתחיל במצב 1, כי אז צריך לא לעשות כלום. לכן נשים בתחילת רשימת הפעולות את הפעולה לבדיקת ערך של משתנה ובה נבדוק אם move_state שווה ל-1. ככה הפעולה הבאה תתבצע רק במצב 1.
אבל הפעולה שצריך לעשות במצב 1 היא לא לעשות כלום! איך לא עושים כלום?
הפסקת ביצוע פעולות
בלשונית control יש פעולה ליציאה מהארוע: . הפעולה הזאת מפסיקה את ביצוע הפעולות של הארוע. ככה כל הפעולות שמופיעות אחריה ברשימה לא יבוצעו. את הפעולה הזו נשים אחרי פעולת הבדיקה, וככה אם המצב הוא 1 אז לא יבוצעו פעולות נוספות.
עכשיו נוכל לטפל במצבים האחרים. גם במצב 0 וגם במצב 2 צריך להזיז את הצורה, לכן נוכל לשים עכשיו את הפעולה לשינוי המשתנה x . היא תתבצע רק אם פעולת הבדיקה נתנה תוצאה שלילית, כלומר: במצבים 0 ו-2.
במצב 2, זה כל מה שצריך לעשות. לכן אחרי הפעולה שמשנה את x, נשים שוב את הפעולה לבדיקת ערך של משתנה והפעם נבדוק אם move_state שווה ל-2. אם כן, לא צריך לבצע פעולות נוספות. לכן אחרי פעולת הבדיקה נשים את הפעולה ליציאה מהארוע .
כל הפעולות הבאות שנשים יבוצעו רק אם שתי פעולות הבדיקה נתנו תוצאה שלילית, כלומר: אם move_state שווה 0.
מה נשאר לעשות במקרה הזה?
- לשנות את הערך של המשתנה move_state ל-1.
- לכוון שעון מעורר חדש ל-10 צעדים.
הנה רשימת הפעולות השלמה בארוע הלחיצה על חץ ימינה:
(הפעולות בארוע לחיצה על חץ שמאלה זהות, מלבד הפעולה לשינוי x.)
נשאר עוד דבר אחד: להוסיף ארוע צלצול שעון מעורר עבור השעון החדש, ובו לשנות את הערך של move_state ל-2 כדי לסמן שהסתיימה ההמתנה שאחרי התזוזה הראשונה.
כשלוחצים על חץ ימינה או שמאלה צריכה להיות המתנה בין התזוזה הראשונה לתזוזות הבאות.
גבול
את האזור שבו הצורות יכולות לזוז נתחום בעזרת אובייקט גבול: אובייקט יציב (Solid) בשם border עם דמות של ריבוע בגודל 16 פיקסלים, לא שקופה (Transparent). נציב אובייקטים כאלה בחדר ככה שייווצר אזור מתאים למשחק, כמו שרואים בתמונה למטה. בדרך כלל רוחב אזור המשחק בטטריס הוא 10 או 12 משבצות, והגובה 18 או 20 משבצות מהתחתית של הצורה.
הגבול לא עוצר את הצורה, כי עוד לא תכנתנו אותם להכיר אחד את השני.
בעבר השתמשנו בארוע התנגשות כדי שאובייקטים לא יעברו את הגבול. הפעם נעשה את זה בדרך אחרת: כמו שהזזנו את הצורה בעצמנו, ככה גם נעצור אותה בעצמנו, בלי להשתמש בארוע ההתנגשות. יש לזה שני יתרונות:
- נשתמש בפעולה לבדיקת התנגשות שמאפשרת לבדוק התנגשות בכל האובייקטים ולא רק בגבול. ככה נוכל לעצור את הצורה בין אם היא מגיעה לגבול ובין אם היא מתנגשת בצורות שנפלו קודם - נוכל לטפל בשני המקרים האלה בבת אחת.
- נצטרך לתכנת בנפרד את העצירה של תנועה למטה, ובנפרד את העצירה של תנועה הצידה. לכאורה זה יותר עבודה, אבל למעשה ההתנהגות בשני המקרים שונה: בתנועה הצידה צריך רק לעצור את הצורה, ואילו בתנועה למטה צריך להפוך את הצורה לחלק מערימת הצורות שכבר נחתו, ואז להפיל מלמעלה צורה חדשה.
כמובן שאפשר לעשות את הדברים האלה גם בארוע התנגשות אם רוצים, אבל במקרה של טטריס נוח יותר לעצור את הצורה בעצמנו.
בשביל לעצור את הצורה בגבול כשלוחצים על החצים צריך ללכת לארועי החצים ימינה ושמאלה ולהוסיף פעולת בדיקה לפני הפעולה שבה משנים את x. ככה הצורה תזוז רק אם הדרך פנויה.
פעולת הבדיקה שבה נשתמש היא הפעולה לבדיקת מקום פנוי שבה השתמשנו גם בפקמן. הפעולה הזו היא ההפך של הפעולה לבדיקת התנגשות: היא בודקת אם מקום מסוים הוא פנוי, ככה שהפעולה הבאה תתבצע רק אם אין בו התנגשות.
בחלון אפשרויות הפעולה צריך לכתוב 16 בשדה x (או -16 אם התנועה היא שמאלה) ו-0 בשדה y. ככה הפעולה תבדוק אם המקום שמימין (או משמאל) פנוי. (כמובן שצריך לסמן Relative.)
את השדה objects אפשר להשאיר על Solid only.
נסו להזיז את הצורה ימינה ושמאלה עד הגבול.
עכשיו צריך לטפל במקרה שהצורה נוחתת למטה על הגבול (או על הערימה של הצורות שנחתו קודם).
נחיתה
כדי לגלות מתי הצורה נוחתת, נלך לארוע צלצול השעון המעורר שבו מפילים את הצורה. לפני הפעולה לשינוי y (שגורמת לצורה ליפול) נכניס פעולה לבדיקת מקום פנוי ובה נבדוק את המיקום שמתחת למיקום הנוכחי, כלומר: Relative, בשדה x הערך 0, ובשדה y הערך 16. ככה הצורה תיפול רק אם היא לא מגיעה לגבול (או לאובייקטים יציבים אחרים, כמו הצורות שנחתו לפניה).
הביאו את הצורה עד למטה ותראו מה קורה.
אחרי שהצורה נוחתת, צריך להפוך אותה לחלק מהערימה של צורות שכבר נחתו, ולהפיל צורה חדשה מלמעלה. הצורות שנחתו הן אובייקט אחר מאובייקט הצורה: הן לא נופלות ולא זזות ימינה ושמאלה כשלוחצים על החצים. לכן נצטרך אובייקט חדש בשבילן. בואו נדחה את זה כרגע, ונעשה רק את החלק השני: להפיל צורה חדשה מלמעלה.
איך נפיל צורה מלמעלה? אפשר ליצור אובייקט צורה חדש שייפול. אבל מאחר שממילא כבר לא צריך את הצורה שנחתה והפכה לערימה, אפשר פשוט להחזיר אותה לנקודת ההתחלה וככה להשתמש בה שוב. ממילא יש לנו כרגע רק צורה אחת, אז בינתיים אפשר להשתמש בה שוב. כשנתכנת צורות אחרות נצטרך לדאוג לשנות את הצורה שנופלת.
את פעולת החזרה לנקודת ההתחלה צריך לעשות במקרה שהפעולה לבדיקת מקום פנוי נתנה תשובה שלילית. כלומר: צריך לשים פעולת בדיקת ההפך ("אחרת") שאחריה נחזיר את הצורה לנקודת ההתחלה. רשימת הפעולות של ארוע צלצול השעון צריכה להראות ככה:
ההערה האחרונה ("צריך: להפוך לערימה") מסמנת שיש שם משהו שעוד צריך לעשות: ליצור במקום הצורה אובייקט (או אובייקטים) שיהיו חלק מהערימה של הצורות שכבר נחתו. כשנגיע לתכנת את הערימה, יהיה אפשר להוסיף מתחתיה את הפעולות שמתאימות, ולמחוק מההערה את המלה "צריך".
כדאי לוודא שאחרי שהצורה נוחתת היא חוזרת ליפול מהתחלה.
הנחתה
בהרבה גירסאות של טטריס אפשר "להנחית" צורה למטה אם לוחצים על מקש רווח. בשביל לתכנת הנחתה נוסיף ארוע הקשה על מקש רווח (Space) . בארוע הזה נרצה להוריד את הצורה עד לגבול שלמטה, או עד לראש הערימה של הצורות שנחתו לפניה. במלים אחרות: להוריד את הצורה עד שהיא תגיע לאובייקט יציב.
בלשונית move יש פעולה שעושה בדיוק את זה: פעולת התזוזה עד לנקודת מגע . הפעולה הזאת מזיזה את האובייקט עד שהוא מגיע לנקודה בה הוא נתקל באובייקט אחר.
בחלון אפשרויות הפעולה יש שלושה שדות:
- direction הוא הכיוון שאליו מזיזים את האובייקט. אנחנו רוצים להזיז אותו למטה, לכן נכתוב שם 270.
- maximum אומר עד כמה רחוק להזיז את האובייקט, במקרה שלא נתקלים בשום אובייקט בדרך. -1 מסמן שאין מגבלה על המרחק של התזוזה, לכן נשאיר את השדה הזה בערך -1.
- against אומר מול איזה מין אובייקטים לבדוק התנגשות - כל האובייקטים או רק היציבים. אנחנו יכולים להסתפק באובייקטים היציבים, לכן נשאיר את השדה הזה על solid objects.
אחרי שהורדנו את הצורה למטה, צריך לגרום לה לנחות - להפוך לחלק מהערימה ולעבור חזרה למעלה כדי ליפול מהתחלה. את זה עשינה כבר בארוע השעון המעורר, לכן במקום לכתוב את זה שוב פשוט נוסיף את הפעולה לכיוון השעון המעורר ונכוון את השעון לצלצל עוד 1 צעדים.
לחיצה על רווח אמורה להנחית את הצורה.
עדכון רשימת העבודה
בואו נראה איפה אנחנו עובדים ברשימת העבודה:
אובייקט ערימה
איזה מין אובייקט (או אובייקטים) צריכים להיות בערימה של הצורות שנחתו?
- צורה שכבר נחתה כבר לא מגיבה יותר ללחיצה על החצים, אלא נשארת במקומה.
- צורות שנחתו מגבילות את התנועה של הצורה הנופלת, ממש כמו הגבול.
- כששורה מתמלאת בצורות שנחתו, כל הריבועים בשורה הזאת נעלמים.
לכן אנחנו יכולים לדעת שני דברים לגבי האובייקט של הערימה:
- הוא יציב.
- הדמות שלו היא ריבוע בגודל 16 פיקסלים, כי כשמשלימים שורה מעלימים ריבועים כאלה (ולא צורות שלמות כמו הצורות שנופלות).
בנוסף, אנחנו רוצים שהריבוע יראה כמו הריבועים של הצורה הנופלת. ככה מה שהמשתמש יראה זה צורה נוחתת, ולא צורה משתנה למשהו אחר.
אם ככה, מה שצריך לעשות זה:
- ליצור דמות חדשה של ריבוע בגודל 16 פיקסלים, שנראית כמו הריבועים של דמות הצורה. הדרך הכי נוח לעשות את זה היא לשכפל את דמות הצורה (להקליק עליה בכפתור הימני של העכבר ואז לבחור Duplicate. אז לעבור לעריכת הדמות החדשה, ושם להקטין את הדף לחצי מהגודל באמצעות Resize Canvas מתפריט Transform. צריך גם לזכור לסמן שהיא לא שקופה (Transparent).
- ליצור אובייקט חדש בשם pile ולתת לו את הדמות החדשה. לסמן שהוא יציב (Solid).
- ללכת לארוע שבו הצורה נוחתת (צלצול שעון מעורר ) ושם ליצור ארבעה מופעים של אובייקט הערימה במקום של הצורה (לסמן Relative):
- אחד בנקודה 0,0
- אחד בנקודה 0,16
- אחד בנקודה 16,0
- אחד בנקודה 16,16
רשימת הפעולות המלאה צריכה להראות ככה:
שימו לב ששיניתי קצת את הסדר, כי צריך ליצור את הריבועים בערימה לפני שמחזירים את הצורה למעלה. אחרת לא נוכל ליצור אותם במקום שאליו הגיעה הצורה.
אחרי שהצורה נוחתת היא נראית כאילו היא נשארת במקום שבו היא נחתה, וצורה חדשה יורדת מלמעלה. הצורות שנוחתות נערמות אחת על השניה.
גובה הערימה
כשהערימה גבוהה וממלאת את אזור המשחק עד למעלה, המשחק נגמר. "למעלה" זה אומר בגובה שבו הצורה מתחילה ליפול, כלומר: במיקום ההתחלתי של הצורה.
המיקום ההתחלתי של אובייקט נשמר בשני משתנים:
- xstart (אִיקְס סְטַרְט) - x בהתחלה.
- ystart (וּוַי סְטַרְט) - y בהתחלה.
כדי לדעת אם הערימה גבוהה מדי, צריך לבדוק את הגובה של אובייקט הערימה ביחס ל- ystart. את זה צריך לעשות כשהצורה נוחתת, כלומר: כשהיא הופכת לחלק מהערימה.
לכן נלך לאובייקט הערימה ונוסיף לו ארוע יצירה . בארוע הזה נשים את הפעולה לבדיקת ערך של משתנה ובה נבדוק אם המשתנה y שווה למשתנה shape.ystart. אם כן, זה אומר שהערימה הגיעה עד לגובה שממנו הצורה מתחילה ליפול.
אם פעולת הבדיקה נותנת תוצאה חיובית, המשחק צריך להגמר. לכן נשים שם בלוק, ובתוכו הפעולות להצגת טבלת האלופים ולהתחלה מחדש של המשחק .
נסו לערום צורות עד למעלה.
[ סקירת העבודה
יצרנו משחק בסיסי עובד. האם נוכל להמשיך ולהוסיף לו דברים עד שיהיה לנו משחק טטריס שלם? או שנצטרך לשנות חלק מהדברים שכבר עשינו לפני שנוכל להמשיך הלאה?
בואו נעבור על מה שכבר עשינו ברשימת העבודה, וננסה לראות אם יש פריטים שנצטרך לשנות, או שיספיק לנו רק להוסיף דברים חדשים. רשימת העבודה כרגע היא:
אובייקט לצורת הריבוע
נפילה
תנועה ימינה ושמאלה כשלוחצים על החצים
להערם בסוף הנפילה
- להעלים שורות שלמות
- לתכנת את שאר הצורות
- לסובב את הצורות כשלוחצים על חץ למעלה
נעבור עליה פריט-פריט:
- אובייקט לצורת הריבוע - האובייקט shape מתאים גם לצורות האחרות, מלבד הדמות שלו. אבל דמות אפשר לשנות (בפעולת שינוי דמות ) אז זו לא בעיה. נצטרך רק להוסיף לו פעולה של סיבוב כשהמשתמש לוחץ על חץ למעלה.
- נפילה - כאן לא נצטרך לשנות כלום. כל הצורות נופלות באותה דרך.
- תנועה ימינה ושמאלה - גם כאן לא נצטרך לשנות כלום, כי התנועה שתכנתנו מתאימה לכל הצורות.
- להערם בסוף הנפילה - כל הצורות יצטרכו להפוך לאובייקט ערימה. אבל בכל זאת, מה שתכנתנו לא מתאים לכל הצורות:
- הצבע של דמות אובייקט הערימה צריך להיות כמו הצבע של דמות הצורה.
- המיקומים שבהם צריך ליצור את המופעים של אובייקט הערימה צריכים להתאים לצורה שנוחתת.
זאת אומרת שאובייקט הצורה והתנועה שלו יכולים להשאר כפי שהם, אבל את הנחיתה של הצורה נצטרך לשנות אם נרצה שהיא תתאים לעוד צורות.
איך לשנות את הנחיתה? מה בדיוק צריך לעשות אחרת? בשביל זה צריך להיות מסוגלים לבדוק את השינויים שאנחנו עושים; רק ככה נוכל לנסות לשנות דברים ולראות אם השינויים באמת עובדים. ובשביל לבדוק איך לשנות את הנחיתה כדי שהיא תתאים לעוד צורות, נצטרך עוד צורות
איך להוסיף עוד צורות
אפשרות אחת לתכנת עוד צורות היא להכין אובייקט לכל צורה. אז יהיו לנו 7 אובייקטים שדומים זה לזה כמעט בכל. ההבדל היחיד יהיה בנחיתה שלהם: בצבע ובמיקום מופעי הערימה שיוצר כל אחד משבעת האובייקטים.
זה פתרון לא טוב. יש בו שתי בעיות עיקריות:
- כל פעם שנרצה להוסיף משהו למשחק או לשנות משהו, נצטרך לעשות את זה בשבעה אובייקטים שונים.
- מדי פעם אנחנו עושים שגיאות. ככל שיש יותר אובייקטים, ככה יש יותר מקומות שאנחנו עלולים להכניס בהם שגיאות. וככה גם יהיה קשה יותר למצוא את השגיאות.
בדרך כלל עדיף שדברים זהים יופיעו רק פעם אחת. לכן עדיף שיהיה לנו רק אובייקט צורה אחד. נוכל להשתמש בו לכל שבע הצורות: פשוט נחליף לו את הדמות ככה שהוא יציג כל פעם צורה אחרת. את הנחיתה של הצורה נצטרך לתכנת בדרך שתתאים לכל שבע הצורות.
איך נתכנת את הנחיתה? כרגיל, בשביל לדעת את זה נצטרך לבדוק את זה ובשביל לבדוק את זה נצטרך לתת לאובייקט הצורה דמות אחרת ולנסות לשנות אותו כך שיעבוד גם איתה. נבחר בדמות הפשוטה ביותר מבין 6 הדמויות שנשארו: דמות הקו הארוך (I).
צורת קו
כדי להתאים את אובייקט הצורה גם לדמות I, נשנה את הדמות שלו בחלון תכונות האובייקט. ככה נוכל לבדוק שהשינויים שנעשה יתאימו לצורת I. כמובן שבסוף נצטרך להגריל דמות באופן אקראי, אבל בואו נשאיר את זה להמשך (ובינתיים נכתוב את זה ברשימת העבודה).
עכשיו נופלים קוים ארוכים אדומים, אבל כשהם נוחתים הם הופכים לערימה של ריבועים תכולים.
יש שני דברים שנצטרך לשנות:
- המיקום של מופעי הערימה שנוצרים כשהצורה נוחתת.
- הצבע שלהם.
בואו נדחה כרגע את הטיפול בצבע (נכתוב את זה ברשימת העבודה) ונטפל קודם במיקום.
מיקום מופעי הערימה
כשהקו נוחת, צריך ליצור טור של ארבעה מופעי ערימה אחד מתחת לשני. איך אפשר לתכנת את הצורה ליצור גם טור של מופעי ערימה (כשנוחת קו) וגם ריבוע של מופעי ערימה (כשנוחת ריבוע)? נצטרך להסתכל בדמות הנוכחית של הצורה, ולפי הדמות ליצור את מופעי הערימה במקומות המתאימים.
יש שני משתנים שיעזרו לנו:
- sprite_width (סְפְּרַייט וּוִידְס') - רוחב הדמות.
- sprite_height (סְפְּרַייט הַייט) - גובה הדמות.
מה שנעשה זה לעבור על הדמות לכל הרוחב וכל האורך, ולמלא אותה במופעי ערימה. כל הרוחב של הקו זה בדיוק כמו מופע אחד של הערימה, וכל הגובה של הקו מספיק לארבעה מופעי ערימה. ככה שאחרי שנעבור על הרוחב והאורך של דמות הקו יהיה לנו טור גבוה של מופעי ערימה. לעומת זאת, דמות הריבוע היא ברוחב שני מופעי ערימה ובגובה שני מופעי ערימה, לכן אחרי שנעבור על דמות הריבוע נקבל שתי שורות שבכל אחת שני מופעי ערימה.
איך עוברים על דמות הצורה לאורך ולרוחב? נתחיל ברוחב - נלך לנחיתה של הצורה (בארוע צלצול שעון מעורר של אובייקט הצורה), נמחק את ארבע הפעולות שיוצרות מופעי ערימה, ובמקומן נוסיף פעולות אחרות:
- נאתחל ל-0 משתנה בשם pile_x. המשתנה הזה יסמן את קורדינטת x של מופע הערימה שאנחנו יוצרים. כל פעם נגדיל אותו ב-16, עד שנכסה את כל הרוחב של דמות הצורה.
- נוסיף פעולת חזרה כדי ליצור לולאה שתחזור לכל רוחב הצורה. מספר הפעמים שהלולאה צריכה לחזור הוא כמו מספר מופעי הערימה שנכנסים ברוחב דמות הצורה, כלומר: רוחב הדמות לחלק ל-16. לכן נכתוב בחלון אפשרויות פעולת החזרה sprite_width / 16.
- בלולאה יהיו כמה פעולות, לכן נשים אחרי פעולת החזרה את הפעולות לתחילת בלוק ולסיום בלוק.
- בתוך הבלוק נשים את הפעולה ליצירת מופע כדי ליצור מופע של הערימה. בשדה x נכתוב pile_x, ובשדה y נכתוב בינתיים 0. כמובן שנסמן Relative כדי שהערימה תיווצר במקום שבו הצורה.
- נגדיל את pile_x ב-16 (לסמן Relative) כדי להתקדם הלאה לרוחב השורה.
כשהצורה נוחתת, השורה העליונה שלה תהפוך לערימה. שנו את דמות הצורה חזרה לריבוע כדי לבדוק את זה גם לדמות הריבוע וגם לדמות הקו.
עכשיו צריך לעשות אותו הדבר גם לגובה. למעשה לא בדיוק אותו הדבר, כי לא נסתפק רק בעמודה השמאלית ובשורה העליונה: אנחנו רוצים את כל השורות וכל העמודות.
לולאה כפולה
הצלחנו לעבור על שורה אחת של הדמות. בשביל לעבור על כל השורות, נצטרך עוד לולאה: לולאה שתחזור שוב ושוב על כל הלולאה של השורה. איך נעשה את זה?
- לפני כל הפעולות שהוספנו קודם, נאתחל ל-0 משתנה בשם pile_y. המשתנה הזה יסמן את קורדינטת y של מופע הערימה שאנחנו יוצרים. כל פעם נגדיל אותו ב-16, עד שנכסה את כל הגובה של דמות הצורה.
- נוסיף פעולת חזרה כדי ליצור לולאה שתחזור לכל גובה הצורה. מספר הפעמים שהלולאה צריכה לחזור הוא כמו מספר מופעי הערימה שנכנסים בגובה דמות הצורה, כלומר: גובה הדמות לחלק ל-16. לכן נכתוב בחלון אפשרויות פעולת החזרה sprite_height / 16.
- בלולאה יהיו הרבה פעולות, לכן נשים אחרי פעולת החזרה את הפעולה לתחילת בלוק. את הפעולה לסיום בלוק נשים אחרי סוף הבלוק של הלולאה שתכנתנו קודם. ככה הלולאה שעוברת על הדמות לגובה תחזור שוב ושוב על הלולאה שעוברת על שורה אחת לרוחב. כלומר: המעבר על כל השורה יתבצע שוב ושוב, מהשורה העליונה ועד לתחתונה.
- נשנה את הפעולה ליצירת מופע: בשדה y נכתוב pile_y.
- אחרי סוף הלולאה שעוברת על שורה, נגדיל את pile_y ב-16 (לסמן Relative) כדי לרדת לשורה הבאה.
כשהצורה נוחתת, נוצרת ערימה שמכסה את כל הצורה בדיוק. כדאי לבדוק את זה גם כשהדמות היא O וגם כשהדמות היא I.
נסתכל רגע מה עשינו כאן: יצרנו לולאה בתוך לולאה - לולאה כפולה. הלולאה החיצונית עוברת מהקצה העליון של הצורה עד התחתית שלה, כל פעם קצת יותר נמוך. בכל חזרה כזאת, הלולאה הפנימית עוברת מהצד השמאלי של הצורה עד הצד הימני. בתמונה משמאל אפשר לראות את הסדר שבו זה קורה כאשר הצורה היא בדמות הריבוע.
צורות אחרות
הצלחנו ליצור ערימה מתאימה גם לדמות O וגם I. מה לגבי הדמויות האחרות? בואו נראה: נשנה את הדמות של אובייקט הצורה ל-T.
מה קורה כשהצורה נוחתת?
הערימה אמנם מכסה את כל הצורה, אבל היא מכסה גם אזורים שקופים. צריך למחוק את מופעי הערימה שנוצרים באזורים השקופים של הצורה.
איך נדע איזה מופעי ערימה נוצרים באזורים השקופים של הצורה? יש דרך פשוטה: לבדוק אם הם מתנגשים בצורה. מופע ערימה שנמצא בחלק השקוף של הצורה, לא מתנגש בה.
איפה נבדוק את ההתנגשות? זה צריך להיות אחרי שמופע הערימה נוצר, אבל לפני שהצורה חוזרת למעלה ומתחילה שוב ליפול. לכן הכי נוח לעשות את זה בארוע היצירה של אובייקט הערימה.
בארוע הזה צריך לבדוק אם מופע הערימה מתנגש באובייקט צורה. בשביל זה נשתמש בפעולה לבדיקת התנגשות באובייקט, שנמצאת בלשונית control: . הפעולה הזאת דומה לפעולה לבדיקת התנגשות, אבל היא בודקת התנגשות רק במופעים של אובייקט מסוים. מאחר שאנחנו רוצים לבדוק התנגשות רק באובייקט הצורה (ולא במופעי ערימה אחרים), הפעולה הזאת מתאימה לנו בדיוק.
בחלון אפשרויות הפעולה נבחר באובייקט הצורה בשדה object. כדי לבדוק התנגשות במיקום הנוכחי של הערימה, נשאיר את השדות x ו-y על 0 ונסמן Relative.
מה נעשה אם יש התנגשות בצורה? כלום. אנחנו מתעניינים רק במקרה שבו אין התנגשות בצורה, כי אז צריך למחוק את המופע. לכן בחלון אפשרויות הפעולה של פעולת הבדיקה נסמן את תיבת הסימון השניה: NOT (נוֹט) - לא.
NOT מסמן להפוך את התוצאה של פעולת הבדיקה. כלומר, במקום שהפעולה הבאה תתבצע רק אם יש התנגשות, היא תתבצע רק אם אין התנגשות. לכן עכשיו נוכל לשים אחרי פעולת הבדיקה את פעולת המחיקה .
מאחר שאנחנו לא רוצים להמשיך לפעולות הבאות, נשים גם את פעולת היציאה מהארוע , ונקיף את שתי הפעולות האלה בבלוק.
מה קורה עכשיו כשהצורה נוחתת? נסו להחליף את דמות אובייקט הצורה גם לשאר הצורות, כדי לבדוק אם הערימה שנוצרת מתאימה לצורה.
המשך העבודה
הצלחנו לתכנת את הנחיתה בדרך שתתאים לכל הצורות, ככה שלא צריך בשבילן אובייקטים נפרדים. בואו נראה מה נשאר לעשות עכשיו:
אובייקט לצורת הריבוע
נפילה
תנועה ימינה ושמאלה כשלוחצים על החצים
להערם בסוף הנפילה
לתכנת את שאר הצורות
- לבחור את צבע הערימה לפי הצורה
- לבחור צורה באופן אקראי
- לסובב את הצורות כשלוחצים על חץ למעלה
-
להעלים שורות שלמות
בחירת דמות לערימה
כשצורה נוחתת, הערימה שנוצרת צריכה להיות באותו צבע כמו הצורה. לכן נצטרך 7 דמויות לערימה - דמות לכל אחת מהצורות. די קל ליצור את התמונות לדמויות האלה לבד, אם משכפלים את הדמויות של הצורות ועורכים אותן קצת. למי שלא רוצה להתעסק עם זה, הכנתי כאן דמויות:
(חשוב לזכור שכל הדמויות האלה צריכות להיות לא שקופות Transparent.)
אחרי שמוסיפים את הדמויות למשחק, אפשר לבחור ביניהן. המקום המתאים לעשות את זה הוא ארוע היצירה של אובייקט הערימה, מיד אחרי הבלוק של בדיקת ההתנגשות בצורה. שם נוכל לבדוק מה הדמות של הצורה, ולפי זה לקבוע את הדמות של מופע הערימה שנוצר.
משתנה הדמות
הדמות של מופע נקבעת במשתנה שנקרא sprite_index (סְפְּרַייט אִינְדֵקְס) - סמן דמות. (ממש כמו שהמיקום נקבע במשתנים x ו-y.) לכן נצטרך לשים את הפעולה לבדיקת ערך של משתנה ובה נבדוק את הערך של סמן הדמות של הצורה - המשתנה shape.sprite_index. בשדה value נכתוב את שם הדמות של צורת הקו. (אני קראתי לדמות הזאת I, אבל אם נתת לה שם אחר אז צריך לכתוב את השם שבו השתמשת.)
אם הבדיקה נותנת תוצאה חיובית, צריך לשנות את הדמות של מופע הערימה לריבוע האדום. אפשר לעשות זאת בפעולת שינוי הדמות , אבל אפשר גם פשוט לשנות את המשתנה sprite_index. בשביל זה צריך לשים את הפעולה לקביעת הערך של משתנה ובה לשנות את המשתנה sprite_index. הערך שצריך לתת לו הוא השם שנתת לדמות הריבוע האדום (אצלי זה pile_red).
קבעו את דמות הצורה לקו האדום כדי לבדוק שהערימה שנוצרת באמת אדומה. קבעו גם דמויות אחרות - שם הערימה עדיין תכולה.
|
עכשיו תורך:
להוסיף פעולות לבדיקת משתנה ולקביעת משתנה גם עבור הדמויות בשאר הצבעים. |
הרבה עבודה לתכנת את כל הבדיקות וקביעות המשתנים האלה, לא? ודאי ישמח אותך לדעת שיש דרך נוחה יותר לעשות את זה. אבל בשביל זה צריך לתכנת את הפעולות שיתבצעו בצורה שונה: בקוד של שפת תכנות.
שפת תכנות
מחשבים לא מבינים עברית. בתוך נבכי המחשב מתרוצצים להם אותות חשמליים שהם השפה היחידה שהמחשב באמת מבין. בני אדם נוהגים לייצג את האותות האלה בסימנים 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: . בפעולה הזאת אפשר לכתוב קוד שיבצע כל דבר שאפשר לבצע בפעולות האחרות, ואפשר לכתוב גם דברים נוספים שאי אפשר לעשות באמצעות הפעולות האחרות. אנחנו נתחיל לאט לאט: קודם נכתוב קוד שעושה דברים שכבר הכרנו, ורק אחר כך נכתוב קוד שעושה דברים חדשים.
נתחיל במשהו פשוט: הפעולה לקביעת הערך של משתנה . נלך לארוע היצירה של אובייקט הערימה, לפעולה שנותנת למשתנה sprite_index את הערך pile_red. נמחק אותה, במקומה נשים פעולת קוד .
חלון אפשרויות הפעולה נראה שונה מאוד לעומת הפעולות האחרות: רוב החלון הוא אזור לבן, שבו אפשר לכתוב את הקוד. נכתוב שם את הקוד הזה:
מה זה אומר?
- הסימן = אומר שזו פעולת קביעת ערך של משתנה, או הצבה, כי "מציבים" ערך אל תוך משתנה.
- משמאל לסימן = כותבים את שם המשתנה שמציבים אליו. במקרה שלנו זה המשתנה sprite_index.
- מימין לסימן = כותבים את הערך שמציבים למשתנה. במקרה שלנו זה pile_red.
- בסוף הפעולה כותבים את הסימן נקודה-פסיק (;) כדי לסמן איפה הפעולה נגמרת. בקוד אפשר לכתוב הרבה פעולות אחת אחרי השניה, ולכן צריך לסמן איפה כל פעולה נגמרת. לכל פעולה כזאת (שנגמרת בנקודה-פסיק) קוראים "פקודה" או "הוראה", כי היא אומרת למחשב מה לעשות.
בין החלקים השונים בקוד מותר לכתוב רווחים, אם רוצים. (כדאי לרצות לכתוב רווחים - זה מקל על הקריאה.)
אחרי שגמרת לכתוב את הקוד הזה, צריך ללחוץ על הכפתור עם ה-וי הירוק (שנמצא למעלה משמאל) כדי לסגור את החלון של הקוד. עכשיו פעולת הקוד תופיע ברשימת הפעולות של הארוע. אם מצביעים עליה בעכבר ומחכים רגע, תצוץ חלונית שמראה את הקוד.
קבעו את דמות הצורה לקו האדום כדי לוודא שהקוד עובד. אם יש תקלות, נסו לקרוא בעיון את הקוד - לפעמים נופלות שגיאות הקלדה, ואז המחשב לא מצליח להבין את הקוד. אם נתת לדמות הריבוע האדום שם אחר, צריך לכתוב את השם שנתת במקום pile_red.
|
עכשיו תורך:
להחליף לפעולות קוד גם את שאר הפעולות של קביעת ערך משתנה. |
פעולות אם-אז בקוד
נמשיך לקוד קצת יותר מורכב: פעולות בדיקה, או "אם-אז". ניקח את הפעולה לבדיקת ערך של משתנה . בקוד היא לא מופיעה לבד, אלא עם הפקודה או הבלוק שאחריה. ניקח פעולת בדיקה עם בלוק פשוט אחריה:
בקוד, נכתוב את הפעולות האלה ככה:
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; היא פקודת הצבה, כמו שראינו קודם. שימו לב ששמתי לפניה רווחים - זה מסמן שהיא בתוך הבלוק. למחשב לא אכפת אם נשים רווחים או לא, אבל בכל זאת כדאי להקפיד לשים רווחים לפני פקודות בתוך בלוק כדי שיהיה קל לראות איפה הבלוק מתחיל ואיפה הוא נגמר. זה חשוב במיוחד במקרה של בלוק בתוך בלוק (למשל אם עושים לולאה כפולה).
בואו נשתמש בקוד הזה: ניצור פעולת קוד ובה נכתוב את הקוד הזה. נשים אותה במקום פעולת הבדיקה ופעולת הקוד להצבה שהשתמשנו בהן קודם למקרה שהצורה היא בדמות הקו.
חשוב: אם נתת שמות אחרים לדמות הקו או לדמות הריבוע האדום, צריך לשנות את הקוד כדי שיופיעו בו השמות האלה, ולא השמות שאני בחרתי.
צריך לבדוק שהנחיתה עדיין עובדת, גם עם הקו וגם עם צורות אחרות.
אפשר להחליף את כל פעולות הבדיקה בצורה דומה, ובמקום כל אחת מהן לשים פעולת קוד. אבל אין צורך בכך: בפעולת קוד אחת אפשר לשים הרבה פקודות, אחת אחרי השניה. את כל פעולות הבדיקה אפשר להחליף בקוד הזה:
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; היא פקודת בקרה מיוחדת: היא גורמת להפסיק את ביצוע הפקודות שאחריה, עד לסוף הבלוק. זה דומה קצת לפעולת היציאה מהארוע , אבל break לא מוציאה מהארוע כולו אלא רק מהבלוק של פקודת switch. אם לא היינו כותבים break, המחשב היה ממשיך לבצע את הפעולות הבאות (למרות שהן מופיעות בתוויות אחרות).
- default: היא התווית האחרונה, שמסמנת מה לעשות אם אף אחת מהתוויות האחרות לא מתאימה. לא חייבים לכתוב את התווית default, אבל בדרך כלל כדאי - יכול להיות שיקרה משהו בלתי צפוי ואז כדאי שנטפל גם במקרה הזה.
|
כדאי לדעת:
קצת מסובך? לא לדאוג - רוב הדברים שנכתוב בקוד יהיו יותר פשוטים מזה. ובמשך הזמן דברים שנראים עכשיו מסובכים ייראו פתאום מובנים וברורים.(למרות שמדובר בקוד פשוט מאוד) |
עכשיו נעתיק לפעולת קוד את הקוד שלמעלה עם פקודת ה-switch, ונמחק את כל פעולות הבדיקה וההצבה שהשתמשנו בהן קודם:
חשוב: אם נתת שמות אחרים לדמויות של הצורה או של הערימה, צריך לשנות את הקוד כדי שיופיעו בו השמות האלה ולא השמות שאני בחרתי.
תנו לאובייקט הצורה דמות שונה בכל פעם כדי לוודא שהנחיתה עובדת בכל הדמויות. מה קורה כשנותנים לו את הדמות של הגבול?
|
כדאי לדעת:
כשכותבים קוד צריכים להקליד הרבה יותר מאשר כשמשתמשים בפעולות הרגילות. אבל מהר מאוד מתרגלים לזה. רוב המתכנתים מקלידים מהר מאוד באנגלית למרות שהם מעולם לא למדו בצורה מסודרת להקליד. הזריזות והדיוק באים עם הזמן והניסיון. |
המשך העבודה
התקדמנו הרבה בידע, אבל השלמנו רק סעיף אחד ברשימת העבודה:
אובייקט לצורת הריבוע
נפילה
תנועה ימינה ושמאלה כשלוחצים על החצים
להערם בסוף הנפילה
לתכנת את שאר הצורות
לבחור את צבע הערימה לפי הצורה
- לבחור צורה באופן אקראי
- לסובב את הצורות כשלוחצים על חץ למעלה
- להעלים שורות שלמות
אל תחששו להתקדם לסעיפים הבאים גם אם לא הכל ברור - רוב מה שנעשה בהמשך יהיה פשוט יותר.
בחירת דמות אקראית
ראינו שאפשר להחליף דמות באמצעות הצבה למשתנה sprite_index. אנחנו רוצים להגריל באופן אקראי אחת משבע הדמויות של הצורות. אבל אנחנו מכירים את הדמויות האלה רק בשמות שלהן: O, I, T וכן הלאה. אם נגריל מספר אקראי באמצעות הפונקציה random() זה לא יעזור לנו לקבל שם של דמות.
מה עושים?
אפשרות אחת היא להשתמש שוב בפקודת switch, ולבחור דמות לפי התוצאה של הפונקציה random().
אבל יש דרך פשוטה יותר. ב-Game Maker יש פונקציה שמגרילה באופן אקראי אחד מהפרמטרים שלה: הפונקציה choose() (צ'וּז) - בחר. למשל, אם נכתוב choose(1, 17, 42) אז נקבל אחד מהמספרים 1, 17 או 42, באופן אקראי.
לכן, מה שצריך לעשות זה:
- ללכת לנחיתה של אובייקט הצורה (בארוע השעון המעורר). ברשימת הפעולות שם, ללכת לפעולה שבה מזיזים את הצורה חזרה לנקודת ההתחלה. מיד אחריה צריך לשנות לצורה את הדמות.
- להוסיף פעולת הצבה ובה להציב במשתנה sprite_index את הערך choose(O, I, T, L, J, S, Z) כדי לקבל אחת מדמויות הצורה באופן אקראי.
- להעתיק את פעולת ההצבה הזאת גם לארוע היצירה של אובייקט הצורה, כדי שגם לצורה הראשונה תהיה דמות אקראית.
עכשיו כל פעם יפלו צורות אחרות, בלי קשר לדמות של אובייקט הצורה.
סידור מחדש של הנחיתה
יש לנו כבר רשימה ארוכה של פעולות שקורות כשצורה נוחתת. ויהיו לנו עוד בהמשך. כרגע כל הפעולות האלה מופיעות בבלוק של פעולת בדיקת ההפך שבארוע השעון המעורר . היה נוח יותר אם היה לנו ארוע לנחיתת הצורה, ואז היינו יכולים להעביר את כל הפעולות האלה לשם.
Game Maker מאפשר לנו ליצור ארועים משלנו, ממש כמו הארועים הרגילים. בשביל להעביר את כל הפעולות של הנחיתה לארוע משלנו, צריך:
- להקליק על Add event ומבין סוגי הארועים לבחור בסוג ארוע Other .
- בתפריט שצץ לבחור ב-User defined ובתת-תפריט לבחור בפריט הראשון: ארוע משתמש מספר 0. עכשיו נוצר ארוע משלנו, ואפשר לשים בו פעולות כמו בכל ארוע אחר.
- ללכת חזרה לארוע השעון המעורר ולבחור את כל הפעולות שבתוך הבלוק של פעולת בדיקת ההפך .
- לקחת אותן משם באמצעות הקשה על Control+X.
- ללכת לארוע המשתמש שלנו, להקליק עם העכבר ברשימת הפעולות שלו, ולהקיש Control+V כדי להדביק את כל הפעולות שלקחנו.
- כדאי להסביר מה בדיוק הארוע הזה: לפני כל הפעולות שהדבקנו, להוסיף פעולת הערה ובה לכתוב "ארוע: נחיתת צורה".
העברנו את הפעולות לארוע שלנו, אבל איך Game Maker ידע מתי הארוע שלנו קורה וצריך לבצע את הפעולות האלה? צריך להגיד לו:
- לחזור לארוע השעון המעורר ולפעולת בדיקת ההפך .
- את הבלוק שאחרי הפעולה הזו אפשר למחוק, כי נצטרך רק פעולה אחת במקרה שהצורה נוחתת: להגיד ל-Game Maker שקרה הארוע שלנו.
- במקום הבלוק, להוסיף פעולת קוד .
- בחלון הקוד לכתוב event_user(0);. הפונקציה event_user אומרת ל-Game Maker שעכשיו קורה ארוע שלנו, ושצריך לבצע את הפעולות בארוע הזה. להבדיל מפונקציות כמו random() או choose(), הפונקציה event_user() לא מחזירה ערך אלא מבצעת פעולה.
אם הסידור מחדש הצליח, המשחק אמור להתנהג בדיוק כמו קודם.
עשינו refactoring - לא שינינו את ההתנהגות של התוכנה, אבל סידרנו אותה בצורה יותר ברורה. עכשיו יהיה לנו נוח יותר להמשיך להוסיף לה דברים.
הצורה הבאה
ברב הגירסאות של טטריס, מראים למשתמש את הצורה הבאה שתיפול כדי שהוא יוכל לתכנן יותר טוב איפה להנחית את הצורה הנופלת. גם אנחנו נעשה ככה.
מה זו הצורה הבאה שמראים למשתמש? היא נראית כמו הצורות הנופלות, אבל ההתנהגות שלה שונה לחלוטין: היא לא נופלת, והיא לא זזה כשלוחצים על החצים, אלא נשארת במקום קבוע על המסך. לכן הצורה הבאה היא אובייקט נפרד. ניצור בשבילה אובייקט ונקרא לו next_shape. נשים מופע של הצורה הבאה בחדר, ליד אזור המשחק.
בארוע היצירה של הצורה הבאה, צריך לתת לה אחת מהדמויות של הצורות באופן אקראי. (אפשר פשוט להעתיק את פעולת ההצבה מארוע היצירה של אובייקט הצורה.)
ליד אזור המשחק תוצג כל פעם צורה אחרת. אבל היא לא משתנה כשהצורה הנופלת נוחתת, ואין כל קשר בינה לבין הצורה הבאה שנופלת.
נצטרך לשנות את מה שקורה בנחיתה של הצורה הנופלת: במקום שהצורה עצמה תשנה דמות באופן אקראי, נרצה שהצורה הבאה תהיה זו שתשנה דמות, והצורה הנופלת תעתיק את הדמות של הצורה הבאה. בשביל זה צריך:
- ללכת לנחיתה של אובייקט הצורה (בארוע המשתמש ). ברשימת הפעולות שם, ללכת לפעולת ההצבה שבה מגרילים דמות חדשה לצורה.
- לפתוח את חלון אפשרויות הפעולה באמצעות הקלקה-כפולה על הפעולה. שם לשנות את המופע שהפעולה מתייחסת אליו (Applies to): לבחור ב-object ושם לבחור באובייקט הצורה הבאה. ככה במקום לשנות את הדמות של הצורה הנופלת, הפעולה תשנה את הדמות של הצורה הבאה.
- לפני פעולת ההצבה הזאת, להכניס פעולת הצבה נוספת ובה להציב אל המשתנה sprite_index את הערך next_shape.sprite_index. ככה הצורה הנופלת תקבל את הדמות של הצורה הבאה.
עכשיו הצורה שליד אזור המשחק מראה מה תהיה הצורה הבאה שתיפול.
המשך העבודה
הנה רשימת העבודה המעודכנת:
אובייקט לצורת הריבוע
נפילה
תנועה ימינה ושמאלה כשלוחצים על החצים
להערם בסוף הנפילה
לתכנת את שאר הצורות
לבחור את צבע הערימה לפי צורה
לבחור צורה באופן אקראי
- לסובב את הצורות כשלוחצים על חץ למעלה
-
להעלים שורות שלמות
תמונות לסיבוב
כדי לסובב צורה נצטרך תמונה שלה בכל אחד מהכיוונים. נתחיל ממשהו פשוט: צורת הקו הארוך. נצטרך גם תמונה של קו עומד וגם תמונה של קו שוכב. נוכל לשים את שתי התמונות יחד באותה דמות. בשביל זה נצטרך לערוך את דמות הקו הארוך: לפתוח את חלון תכונות הדמות של הקו (באמצעות הקלקה כפולה על הקו ברשימת המשאבים), וללחוץ על הכפתור Edit Sprite.
עכשיו צריך לשנות את גודל הדמות כדי שהיא תהיה מספיק רחבה להתאים גם לקו שוכב:
- להכנס לעריכת התמונה באמצעות הקלקה-כפולה על התמונה.
- ללכת לתפריט Transform ולבחור Resize Canvas.
- בחלון שנפתח, להוריד את הסימון מתיבת הסימון Keep aspect ratio כדי שנוכל לשנות את הדף מצורת מלבן ארוך לצורת ריבוע.
- בשדה הימני של רוחב הדף (Width) לשנות את הערך מ-16 פיקסלים ל-64 פיקסלים. אז להקליק OK.
- אם הקו החדש מופיע באמצע התמונה, צריך להזיז אותו, כדי שיתאים למיקום של שריג 16, כמו שאר הצורות. את זה אפשר לעשות באמצעות לחיצה על החצים הכחולים שבסרגל הכלים. אפשר להזיז אותו עד לקצה השמאלי או הימני, ואפשר להזיז אותו רק 8 פיקסלים, ואז הוא יהיה במרחק 16 פיקסלים מהקצה.
- ללחוץ על ה-וי הירוק.
עכשיו אפשר להוסיף לדמות עוד תמונה, שבה יהיה הקו השוכב. ל-Game Maker יש דרך מובנית לעשות את זה. בשביל זה צריך:
- ללכת לתפריט Animation.
- לבחור Rotation Sequence. בתת-תפריט שצץ לבחור Clockwise.
- השדה הראשון אומר כמה תמונות יהיו ברצף - לכתוב שם 2 כי הקו יכול להסתובב רק לשתי צורות: שוכב ועומד.
- השדה השני אומר איזה זווית צריך לכסות בכל הסיבובים האלה. שם צריך לכתוב 180. (360 מעלות זה סיבוב מלא, אבל במקרה של הקו אחרי חצי סיבוב הוא שוב חוזר להיות קו עומד, אז מספיק 180 מעלות.)
קיבלנו שתי תמונות קו. אפשר להחליף את הסדר שלהן אם רוצים: להקליק על אחת התמונות, ואז ללחוץ על החצים שבצד ימין של סרגל הכלים. ככה אפשר לקבוע איזו תמונה תופיע ראשונה. (בדרך כלל בטטריס הקו מופיע שוכב.)
אחרי שגמרנו, אפשר ללחוץ על ה-וי הירוק ולבדוק מה יצא.
מה קורה כשמוצג קו?
הדמות של הקו מוצגת מונפשת - התמונות שלה מתחלפות במהירות. צריך לשנות את מהירות ההנפשה ל-0 כדי שהדמות לא תזוז. אפשר לעשות את זה באמצעות הפעולה לשינוי דמות , אבל אפשר גם לקבוע את הערך של המשתנה image_speed (אִימֵג' סְפִּיד) - מהירות תמונה. לכן נלך לארוע היצירה של אובייקט הצורה ונוסיף שם פעולת הצבה שבה נציב במשתנה image_speed את הערך 0. את הפעולה הזאת צריך להעתיק גם לארוע היצירה של אובייקט הצורה הבאה.
עכשיו הקו נראה בסדר.
הפעלת הסיבוב
הצורה צריכה להסתובב כשהמשתמש לוחץ על חץ למעלה. לכן נוסיף לאובייקט הצורה ארוע הקשה על מקש חץ למעלה (Up) . בארוע הזה צריך לשנות את התמונה המוצגת מתוך התמונות בדמות. גם את זה אפשר לעשות באמצעות הפעולה לשינוי דמות , אבל גם הפעם נעדיף לעשות את זה באמצעות הצבה למשתנה שקובע את התמונה המוצגת: image_index (אִימֵג' אִינְדֵקְס) - סמן תמונה. לכן נוסיף פעולת הצבה ובה נגדיל את ב-1 את image_index (לסמן Relative).
מה קורה אם לוחצים על חץ למעלה כשנופל קו? ומה קורה כשמופיע הקו הבא?
כשהדמות מתחלפת, זה לא משנה את המשתנה image_index. אם רוצים שהקו יופיע תמיד באותה צורה, צריך להחזיר את הערך של image_index ל-0 כשמחזירים את הצורה למעלה:
- ללכת לנחיתה של הצורה (בארוע המשתמש ).
- אחרי הפעולה שמחזירה את הצורה חזרה למעלה, להוסיף פעולת הצבה ובה לשנות את image_index חזרה ל-0.
עכשיו הקו תמיד יתחיל ליפול באותה צורה.
|
כדאי לדעת:
בפעולה לשינוי דמות יש שלושה שדות: sprite, subimage, ו-speed. ראינו שאפשר לשנות כל אחד מהם גם באמצעות פעולת הצבה למשתנה : sprite_index, image_speed, ו-image_index.
כלומר: פעולת שינוי הדמות פשוט קובעת את הערכים של שלושת המשתנים האלה.
יש עוד פעולות כאלה, שפשוט קובעות ערכים של משתנים. למשל:
- פעולת תנועה מדוייקת קובעת את הערך של המשתנים direction ו- speed.
- פעולת העברה למקום אחר קובעת את הערך של המשתנים x ו-y.
- פעולת קביעת התנועה האופקית קובעת את הערך של המשתנה hspeed.
|
התנגשות
בואו נעשה נסיון: תפעילו את המשחק, חכו שייפול קו, סובבו אותו ככה שיעמוד, ואז הזיזו אותו שמאלה עד הסוף - עד לגבול. אז נסו ללחוץ על חץ למעלה כדי לסובב אותו. מה קורה?
אנחנו מרשים לצורה להסתובב גם אם יש התנגשות. צריך לבטל את הסיבוב במקרה של התנגשות:
- ללכת לארוע הלחיצה על חץ למעלה .
- אחרי פעולת ההצבה, לשים את הפעולה לבדיקת התנגשות כדי לבדוק אם הצורה מתנגשת במשהו (גבול או ערימה). לא לשכוח לסמן Relative כדי לבדוק התנגשות במקום שבו הצורה נמצאת.
- אם יש התנגשות, לבטל את הסיבוב: להוסיף פעולת הצבה ובה להציב אל המשתנה image_index את הערך -1 ולסמן Relative.
מה קורה עכשיו כשמנסים לסובב את הקו ליד הגבול?
שאר הצורות
עכשיו נוכל להוסיף תמונות גם בשביל שאר הצורות.
|
עכשיו תורך:
להוסיף תמונות מסובבות לדמויות S ו-Z, כמו שעשינו לקו. (בדמויות האלה צריך להגדיל את הנייר ל-48 פיקסלים.)
|
את הצורות L, J ו-T צריך לסובב סיבוב שלם. אחרי שמגדילים את הנייר שלהן ל-48 פיקסלים, צריך לבחור שבהנפשת הסיבוב יהיו 4 תמונות ו-360 מעלות.
עכשיו אפשר לסובב את כל הצורות.
המשך העבודה
השלמנו כמעט את כל הרשימה:
אובייקט לצורת הריבוע
נפילה
תנועה ימינה ושמאלה כשלוחצים על החצים
להערם בסוף הנפילה
לתכנת את שאר הצורות
לבחור את צבע הערימה לפי צורה
לבחור צורה באופן אקראי
לסובב את הצורות כשלוחצים על חץ למעלה
-
להעלים שורות שלמות
מציאת שורות מלאות
כשנוצר מופע ערימה חדש, הוא עשוי למלא חלל ככה שתיווצר שורה מלאה במופעי ערימה. אם שורה מתמלאת במופעי ערימה, צריך להעלים אותה ולהעלות את הניקוד.
איך נדע אם השורה התמלאה? נצטרך לבדוק את זה בארוע היצירה של אוביקט הערימה . בסוף רשימת הפעולות של הארוע הזה, נוסיף הערה "לבדוק אם השורה מלאה". מתחת להערה הזאת נוסיף את הפעולות שיבדקו אם השורה מלאה.
איזה פעולות נצטרך? כדי לגלות אם השורה מלאה, נרצה ללכת ימינה משבצת-אחר-משבצת ולבדוק אם בה ערימה או רווח. אם נגיע לגבול ועדיין לא נמצא רווח, נלך שמאלה משבצת-אחר-משבצת ונחפש רווח. אם נגיע לגבול השמאלי בלי למצוא רווח, נדע שהשורה מלאה. אם נמצא בדרך רווח, נדע שהשורה לא מלאה ונוכל להפסיק לבדוק את שאר המשבצות.
זה אומר שנצטרך שתי לולאות אחת אחרי השניה: לולאה אחת בשביל ללכת ימינה עד הגבול, ולולאה שניה בשביל ללכת שמאלה עד הגבול.
אבל כמה פעמים צריכה לחזור כל לולאה? אנחנו לא יודעים למצוא את x של הגבול מימין ומשמאל, ככה שאנחנו לא יכולים לחשב כמה משבצות צריך לבדוק מימין ומשמאל. אנחנו יכולים רק ללכת הצידה משבצת-אחר-משבצת ולראות אם כבר הגענו לגבול (באמצעות הפעולה לבדיקת התנגשות באובייקט ).
לכן לא נוכל להשתמש בפעולת החזרה . נצטרך לתכנת לולאה בדרך אחרת.
לולאת 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
בשביל לבדוק אם הגענו לגבול בלי קוד, משתמשים בפעולה לבדיקת התנגשות באובייקט . בקוד, אפשר להשתמש בפונקציה שבודקת את זה: הפונקציה 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; היא מילת מפתח שמסמנת פקודת יציאה מהקוד, בדומה לפעולה ליציאה מהארוע . באנגלית המילה 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;
}
נבדוק אם זה עובד: נשים פעולת קוד בסוף ארוע היצירה של הערימה , ונעתיק אליה את הקוד מלמעלה. כדי לראות אם הלולאה הסתיימה או שיצאנו ממנה באמצע, צריך לעשות משהו אחרי סוף הבלוק. הכי פשוט יהיה להשמיע צליל, ואז כשנשמע את הצליל נדע שלולאה הסתיימה ולא יצאנו ממנה באמצע.
לכן ניצור משאב צליל חדש בשם full, ואז נוסיף בסוף הקוד את השורה: sound_play(full);. הפונקציה sound_play גורמת להשמעת צליל, ממש כמו הפעולה להשמעת צליל . הקוד המלא בפעולת הקוד יהיה:
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);
נסו להזיז צורה ימינה עד הגבול - כשהיא תנחת אמור להישמע צליל. נסו להנחית צורה באמצע או משמאל - כשהיא תנחת לא אמור להיות צליל.
הצד השני
בדקנו אם השורה מלאה מימין למופע ערימה. נוכל לבדוק באופן דומה את צד שמאל. הקוד לבדיקת הצד השמאלי של השורה דומה מאוד לקוד שהשתמשנו בו קודם. ההבדלים היחידים הם בפעולות ההצבה: במקום לזוז ימינה באמצעות הוספת 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;
}
את הקוד הזה צריך להוסיף לפעולת הקוד שהשתמשנו בה קודם (לפני הפקודה שמשמיעה צליל).
עכשיו הצליל אמור להישמע רק כאשר מתמלאת שורה שלמה.
|
כדאי לדעת:
מה קורה כשהצורה שנחתה יוצרת כמה מופעי ערימה באותה השורה?
כאשר מופע הערימה הראשון יתחיל לבדוק אם השורה מלאה, הוא יגלה שהמשבצת שלידו ריקה (כי עדיין לא נוצר שם מופע ערימה) ויפסיק את הבדיקה. רק כשהמופע האחרון ייווצר, הוא ימצא את כל המופעים שנוצרו לידו. לכן רק המופע האחרון ישלים את השורה וישמיע את הצליל. |
|
כדאי לדעת:
איך מגלים איזה פונקציות ופקודות אפשר לכתוב בקוד?
מי שיודע אנגלית, יכול למצוא את כל הפונקציות והפקודות בעזרה של Game Maker, בחלק שנקרא The Game Maker Language. |
שורה מלאה
כששורה מתמלאת, היא צריכה להבהב כמה פעמים, ואז כל מופעי הערימה שבה צריכים להימחק וכל המופעים שמעליהם צריכים לרדת שורה.
איך נתכנת את ההתנהגות הזאת? יש שתי אפשרויות:
- לתכנת אותה באובייקט הערימה.
- לתכנת אותה באובייקט חדש שיהיה אחראי לשורות מלאות.
שתי האפשרויות טובות ומתאימות. לטעמי, האפשרות השניה (ליצור אובייקט חדש לשורה מלאה) היא ברורה ונקיה יותר, ולכן בחרתי בה. אבל אפשר לבחור גם אחרת.
אז ניצור אובייקט חדש בשם row. ניתן לו בהתחלה את הדמות של הגבול, רק בשביל לבדוק אותו - אחר כך נשנה את זה. אנחנו צריכים ליצור מופע של אובייקט השורה כל פעם שהערימה ממלאת שורה שלמה. זה קורה בפעולת הקוד שבסוף ארוע היצירה של הערימה . נוסיף עוד פקודה בסוף הקוד, אחרי הפקודה שמשמיעה צליל:
instance_create( x, y, row );
הפונקציה instance_create זהה לפעולה ליצירת מופע . הקוד שכתבנו יוצר מופע של אובייקט row במיקום הנוכחי של הערימה (x,y).
כל פעם שמשלימים שורה אמור להופיע ריבוע על מופע הערימה שהשלים את השורה.
הצלחנו ליצור מופע שורה כל פעם שמתמלאת שורה בערימה, אבל המופע שיצרנו לא נראה כמו שורה אלא רק כמו ריבוע.
גודל השורה
השורה צריכה להתחיל במופע הגבול השמאלי ולהימשך עד לימני. איך נדע איפה בדיוק ליצור אותה וכמה רחבה היא צריכה להיות?
אנחנו כבר יודעים איפה הגבול השמאלי: ב-x_check. הרי הלולאה השניה נגמרה בדיוק כשהגענו לגבול השמאלי, לכן אחריה x_check עדיין אומר איפה הגבול השמאלי. לכן נרצה ש-x של השורה יהיה x_check + 16 כדי שהיא תוצב בדיוק משבצת אחת ימינה מהגבול. בשביל זה נשנה את השורה האחרונה של הקוד לשורה הזאת:
instance_create( x_check+16, y, row );
עכשיו הריבוע אמור להופיע בצד שמאל של השורה שמשלימים.
מצאנו את המקום המתאים לשורה, עכשיו צריך למצוא את הגודל. בשביל זה צריך לדעת איפה הגבול הימני.
|
כדאי לדעת:
למה לא לתת לשורה גודל קבוע, לפי הגודל שציירנו את הגבול בחדר?
גודל קבוע יקשה לשנות בעתיד את רוחב אזור המשחק. אם השורה תהיה בגודל קבוע אז לא נוכל ליצור חדרים שונים שבכל אחד מהם רוחב שונה. וגם לא נוכל ליצור אזור משחק לא מלבני (למשל בצורת משולש הפוך).
בקיצור: גודל שורה קבוע מראש יגביל את היכולת שלנו לשנות ולשפר את המשחק. |
אנחנו יכולים לדעת איפה הגבול הימני אחרי הלולאה הראשונה, כי אז 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;
האם השורה בגודל המתאים?
לא שינינו את גודל השורה אלא את גודל מופע הערימה, כי המשתנה image_xscale שהצבנו אליו שייך למופע הערימה שבו מבוצע הקוד ולא למופע השורה שיצרנו בשורה הקודמת.
אז אולי במקום לכתוב image_xscale נכתוב row.image_xscale כמו שעשינו במשחקים הקודמים? הבעיה עם זה היא שכאשר יש יותר ממופע אחד של row, הצבה למשתנה row.image_xscale תשנה את image_xscale בכל המופעים של row. אנחנו רוצים לשנות רק את השורה שיצרנו, כי יכול להיות שיש שורות אחרות ברוחב אחר (אם בוחרים לצייר אזור משחק לא מלבני ומשלימים כמה שורות בבת אחת). לכן אנחנו צריכים למצוא דרך לכתוב image_xscale של המופע המסוים שיצרנו.
זיהוי מופעים
לכל מופע במשחק יש מספר זיהוי. אפשר לראות את המספר הזה בחלון תכונות החדר: מצביעים על מופע עם העכבר, ואז אפשר לראות את מספר הזיהוי שלו למטה בשורת המצב.
מספר הזיהוי הזה הוא כמו שם פרטי של מופע, ואפשר להשתמש בו כמו שמשתמשים בשם האובייקט. למשל, אפשר לכתוב:
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 של המופע החדש שנוצר.
עכשיו דמות השורה מכסה את כל השורה שהושלמה.
הבהוב
במשחק פקמן ראינו כבר שהבהוב הוא פשוט סוג של הנפשה. לכן צריך ליצור דמות חדשה בשביל אובייקט השורה, ולערוך אותה ככה שהיא תיראה מהבהבת.
|
עכשיו תורך:
ליצור לאובייקט השורה דמות חדשה בגודל 16 פיקסלים, ולגרום לה להבהב כמו שעשינו במשחק פקמן, אבל יותר מהר. |
השורה צריכה להבהב כמה פעמים ואז להעלם. איך נספור את ההבהובים? אפשרות אחת היא באמצעות שעון מעורר. אבל יש עוד אפשרות נוחה: כל פעם שדמות גומרת להציג את התמונה האחרונה ברצף ההנפשה וחוזרת לתמונה הראשונה, קורה ארוע סוף ההנפשה (Animation end) . נוסיף את הארוע הזה לאובייקט השורה ונשים בו את הפעולה למחיקת אובייקט כדי למחוק את השורה.
השורה מהבהבת פעם אחת ודי.
כדי שהשורה תהבהב כמה פעמים לפני שהיא נמחקת, נוסיף עוד תמונות לדמות המונפשת שלה:
- נלך לחלון עריכת הדמות.
- נקליק על אחת התמונות ונקיש Control+C כדי להעתיק אותה.
- נקיש Control+V כדי להדביק עותק שלה.
- נעשה אותו הדבר לתמונה השניה.
- נשנה את סדר התמונות ככה שהצבע יתחלף בין תמונה לתמונה כדי שיהיה הבהוב.
- נעתיק ונדביק שוב כדי שבסך הכל ההנפשה תכלול שלושה הבהובים.
השורה מהבהבת שלוש פעמים.
העלמת השורה
אחרי שהשורה מהבהבת, היא צריכה להעלם: למחוק את כל מופעי הערימה שבשורה, ולהוריד משבצת אחת למטה את כל מופעי הערימה שמעליה.
בשביל זה צריך לעבור על כל מופעי הערימה ולבדוק את הגובה של כל אחד מהם: אם הוא גבוה מהשורה צריך להנמיך אותו במשבצת, ואם הוא בגובה השורה בדיוק אז צריך למחוק אותו. כלומר, צריך לולאה שעוברת על כל מופעי הערימה. בשפת GML יש לולאה מתאימה.
לולאת with
כשרוצים לחזור על פקודות מסויימות עבור כל המופעים של אובייקט מסוים, משתמשים בלולאת with. למשל:
with (pile)
{
y = y + 16;
}
מה שזה אומר:
- with היא מילת מפתח שמסמנת פקודה ללולאה שחוזרת עבור כל אחד מהמופעים של אובייקט מסוים.
- הביטוי בסוגריים (pile) שאחרי המלה with מסמן את האובייקט שעל המופעים שלו הלולאה תעבור.
- סימני הצומדיים { ו-} מסמנים תחילת וסוף בלוק.
- y = y + 16; היא פקודת הצבה, שמגדילה ב-16 את y של המופע שעליו חוזרת הלולאה. כלומר, כל פעם מדובר ב-y של מופע ערימה אחר. זה לא y של המופע שבו מבצעים את פעולת הקוד.
בעברית אפשר לנסח את הקוד הזה ככה: "עם כל אחד מהמופעים של אובייקט הערימה, תגדיל את y ב-16." בואו נראה איך זה עובד: נוסיף פעולת קוד עם הקוד הזה, לארוע סוף ההנפשה של השורה .
מה קורה כשמתמלאת שורה שלמה?
משתנים בתוך לולאת 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;
}
עכשיו פקודת ההצבה תתבצע רק למופעי הערימה שגבוהים מהשורה.
נסו למלא שורה שלמה ולראות מה קורה עכשיו.
הורדנו את כל מופעי הערימה שמעל השורה, אבל צריך גם למחוק את המופעים שבאותו גובה כמו השורה.
מחיקת מופע בקוד
כמו שיש פעולה למחיקת מופע , יש גם פונקציה שמוחקת מופע: instance_destroy(). הפונקציה instance_destroy פשוט מוחקת את המופע הנוכחי. בתוך לולאת with היא מוחקת את המופע שהלולאה עוברת עליו כרגע.
אנחנו רוצים למחוק רק מופעי ערימה שבאותו גובה כמו השורה, לכן נוסיף בתוך הלולאה את הקוד המודגש:
with (pile)
{
if ( y == other.y )
instance_destroy();
if ( y < other.y )
y = y + 16;
}
הקוד המודגש אומר: אם y של המופע שהלולאה עוברת עליו כרגע שווה ל-y של השורה, אז תמחק את המופע הזה.
עכשיו השורה תעלים את כל מופעי הערימה שבה.
המשך העבודה
השלמנו את כל רשימת העבודה. המשחק עובד ואפשר לשחק בו. מה שנשאר זה להציג לוח ניקוד יפה, רקע מתאים, ואריזה גרפית כמו שעשינו במשחק חייזרים. כדאי גם לעשות את המשחק מאתגר יותר: להאיץ את מהירות הנפילה של הצורות, כל פעם קצת.
לכן רשימת העבודה החדשה תהיה:
- לוח ניקוד
- האצת מהירות הנפילה
- רקע
- אריזה
לוח הניקוד
ברוב הגירסאות של טטריס לוח הניקוד מציג שלושה דברים:
- הניקוד
- מספר השורות שהושלמו
- הרמה
גם אנחנו ניצור לוח ניקוד שמציג את שלושת הדברים האלה.
|
עכשיו תורך:
ליצור אובייקט score_panel שיציג את הניקוד, ולהוסיף מופע שלו לחדר. לתת לו להציג מתחת לניקוד עוד שני משתנים גלובליים: אחד בשם lines והשני בשם level. לאתחל את שני המשתנים האלה ל-0 בארוע Game start . |
את הניקוד ואת מספר השורות צריך להגדיל בכל פעם שנעלמת שורה. לכן בארוע סוף ההנפשה של השורה צריך להוסיף את הפעולה לקביעת הניקוד ואת פעולת ההצבה כדי להגדיל את הניקוד ואת מספר השורות global.lines.
הניקוד ומספר השורות צריכים לעלות כשמשלימים שורות.
גם כשמנחיתים צורה הניקוד צריך לעלות בהתאם לגובה ההנחתה. לכן נלך לארוע הקשה על מקש רווח של אובייקט הצורה, ונוסיף בסופו את הפעולה לקביעת הניקוד . כמה ניקוד נוסיף? לפי גובה הנחיתה. הגובה הזה הוא ההפרש בין y לבין yprevious. לכן נכתוב בחלון אפשרויות הפעולה (y - yprevious) / 16 וכמובן נסמן Relative.
הניקוד צריך לעלות גם כשמנחיתים צורה.
בהזדמנות זו, נוכל לעשות עוד משהו בארוע ההקשה על רווח: כרגע ההנחתה מכוונת את השעון המעורר, ואז ארוע השעון המעורר מפעיל את ארוע המשתמש, ושם הצורה הופכת לערימה. במקום זה, אפשר להפעיל את ארוע המשתמש ישירות בהנחתה. בשביל זה צריך:
- למחוק את הפעולה לכיוון שעון מעורר.
- במקומה לשים פעולת קוד, שבה הפקודה event_user(0);.
המשחק אמור להתנהג אותו הדבר כמו קודם, כי עשינו רק refactoring.
הרמה
אחרי כל עשר שורות שמשלימים, עולה הרמה. ככל שהרמה גבוהה יותר מקבלים יותר נקודות וגם המשחק נעשה יותר מהיר. זה אומר שנצטרך לעשות שלושה דברים:
- להעלות רמה כל עשר שורות.
- לשנות את הפעולות לקביעת ניקוד ככה שיתנו ניקוד גבוה יותר לפי הרמה.
- לשנות את מהירות נפילת הצורה לפי הרמה.
נתחיל בהעלאת הרמה: נלך להעלמת שורה בארוע סוף ההנפשה של השורה. בסוף רשימת הפעולות שלו צריך להוסיף פעולת בדיקה כדי לבדוק אם מספר השורות שהושלמו מתחלק ב-10 (למשל: 10, 20, 30, או אפילו 100) כי אז צריך לעלות רמה. איזה פעולת בדיקה תוכל לבדוק את זה? הפעולה לבדיקת ערך של משתנה לא תתאים, כי אנחנו לא רוצים לבדוק רק ערך אחד אלא סוג שלם של ערכים: כל הערכים שמתחלקים ב-10. לכן נשתמש בפעולת בדיקה אחרת בלשונית control: הפעולה לבדיקת ביטוי .
הפעולה לבדיקת ביטוי מאפשרת לבדוק ביטוי שרוצים (ממש כמו פקודת if בקוד). אנחנו צריכים ביטוי שאומר אם מספר מתחלק ב-10. אין ב-GML פונקציה שבודקת את זה, אבל יש אפשרות לחשב את השארית שנשארת כשמחלקים מספר ב-10, באמצעות פעולת החשבון mod. הביטוי 27 mod 10 ייתן את השארית שנשארת כשמחלקים 27 ב-10, כלומר: 7.
לכן בחלון אפשרויות הפעולה לבדיקת ביטוי נכתוב global.lines mod 10 == 0 כי הביטוי הזה יהיה נכון רק אם מספר השורות מתחלק ב-10 בדיוק (השארית שווה ל-0).
אם הבדיקה מצליחה, צריך להעלות את הרמה: להגדיל את המשתנה global.level ב-1.
נסו להשלים שורות ולראות אם הרמה בלוח הניקוד עולה בהתאם.
קודים לבדיקות
לוקח הרבה זמן לבדוק שהרמה באמת מתקדמת כל 10 שורות. ובהמשך נצטרך לעלות רמות שוב ושוב כדי לבדוק את האצת קצב הנפילה ואת חישוב הניקוד ברמות גבוהות. היה נוח יותר אילו יכולנו להגדיל את מספר השורות או את הרמה במהלך המשחק כדי לבדוק את הדברים האלה. הכי קל לעשות את זה באמצעות קודים, כמו שעשינו במשחק פקמן: להוסיף ארוע הקשה על מקש שיגרום להגדלת מספר השורות.
בשביל לעשות את זה נוסיף לאובייקט לוח הניקוד ארוע הקשה על איזשהו מקש , למשל Control (אפשר לבחור משהו אחר אם רוצים). בארוע הזה נוסיף פעולת הצבה ובה נגדיל את global.lines ב-1. ככה נוכל להגדיל את מספר השורות ל-9 או 19 או 29 במהירות, ואז לבדוק בקלות את עליית הרמה.
עכשיו אפשר לבדוק גם עליה לרמות גבוהות.
ניקוד לפי הרמה
הניקוד על השלמת שורה והנחתת צורה צריך להיות תלוי ברמה: ברמה 0 מקבלים 40 נקודות לשורה, ברמה 1 מקבלים 80 נקודות לשורה, ברמה 2 מקבלים 120 נקודות לשורה, וכן הלאה. במלים אחרות, כל פעם שמשלימים שורה (בארוע סוף ההנפשה של השורה ) צריך להוסיף לניקוד 40 * (global.level + 1).
גם את הניקוד על הנחתת צורה צריך להכפיל ב-(global.level + 1). כלומר, בארוע הקשה על רווח של הצורה צריך לשנות את פעולת קביעת הניקוד כך שתוסיף (y - yprevious) / 16 * (global.level + 1) נקודות.
ככל שהרמה יותר גבוהה, השלמת שורות והנחתת צורות תתן יותר נקודות.
מהירות הנפילה
כל פעם שעולים רמה, הצורה צריכה ליפול קצת יותר מהר. זה אומר שבמקום לקבוע שעון מעורר לעוד 20 צעדים כדי ליפול משבצת, הצורה צריכה משתנה שיגיד כמה צעדים לחכות עד לנפילה הבאה. כשהרמה עולה, נקטין את הערך של המשתנה הזה.
בשביל זה צריך:
- ללכת לארוע היצירה של הצורה.
- לשים בתחילתו פעולת הצבה, ובה לאתחל ל-20 משתנה בשם delay.
- לשנות את פעולת כיוון השעון המעורר ככה שהשעון יצלצל עוד delay צעדים (במקום 20).
- ללכת לארוע השעון המעורר שבו מתבצעת הנפילה של הצורה.
- גם כאן לשנות את פעולת כיוון השעון המעורר ככה שהשעון יצלצל עוד delay צעדים במקום 20.
המשחק אמור להתנהג כמו קודם, כי בינתיים רק עשינו refactoring בלי לשנות התנהגות.
עכשיו צריך להאיץ את קצב הנפילה בכל פעם שעולים רמה:
- ללכת לארוע סוף ההנפשה של אובייקט השורה.
- הפעולה לבדיקת ביטוי אומרת מתי עולים רמה, לכן צריך להוסיף אחריה את הפעולות להתחלת וסוף בלוק. (לשמור שפעולת ההצבה שמעלה את הרמה תהיה בתוך הבלוק הזה.)
- להוסיף לבלוק עוד פעולת הצבה, ובה להקטין ב-1 את המשתנה delay של הצורה: shape.delay.
עכשיו המשחק אמור להיות קצת יותר מהיר כל פעם שעולים רמה.
מה קורה אם מגיעים לרמה 20? המשתנה delay יהיה 0! לכן לפני שמקטינים אותו צריך להכניס פעולה לבדיקת ערך של משתנה ובה לוודא שהוא גדול מ-2 או אולי אפילו 3.
עכשיו המשחק לא ייתקע כשמגיעים לרמות גבוהות.
המשך העבודה
בואו נראה איפה אנחנו ברשימת העבודה:
לוח ניקוד
האצת מהירות הנפילה
- רקע
- אריזה
עיצוב החדר
כרגע יש לנו חדר לא מאוד יפה, אבל עם משחק עובד. בואו נעצב את החדר כדי שייראה יותר יפה.
קודם כל, החדר רחב מדי - במשחק לא משתמשים בכל הרוחב. לכן כדאי לשנות את הרוחב שלו:
- ללכת לחלון תכונות החדר.
- לעבור ללשונית settings.
- לשנות את הערך שכתוב בשדה Width כדי שהחדר לא יהיה רחב מדי.
- אם החדר נראה לך גבוה מדי, אפשר לשנות גם את הערך שכתוב בשדה Height.
עכשיו נוסיף לו רקע . אפשר לבחור איזו תמונה שרוצים - תמונה שצילמת או תמונה מפליקר. כדאי רק לדאוג להתאים את הגודל שלה לגודל החדר.
איך המשחק נראה עכשיו?
ברוב התמונות, קצת קשה לראות את לוח הניקוד, וגם אזור המשחק והצורה הבאה פחות ברורים כשיש רקע. לכן כדאי לצייר על הרקע מלבנים בצבע לבן באזורים שעליהם יוצגו אזור המשחק, הצורה הבאה, ולוח הניקוד.
איך המשחק נראה עכשיו?
אובייקט הגבול לא מוסיף ליופי של החדר. עכשיו גם ממילא אפשר לדעת איפה גבול אזור המשחק, לפי הרקע. לכן כדאי להפוך את הגבול לאובייקט נסתר: להוריד לו את הסימון מתיבת הסימון Visible.
יותר יפה, לא?
מוזיקת רקע
טטריס הוא משחק בלי פיצוצים או כל מיני אפקטים מיוחדים. לכן נהוג לשים בו מוזיקת רקע, כדי להעסיק גם את חוש השמיעה בזמן המשחק. השמעת מוזיקת רקע ב-Game Maker דומה להשמעת צליל רגיל: מוסיפים משאב צליל ומשמיעים אותו בפעולה להשמעת צליל . יש רק שני הבדלים:
- סוג הקובץ של הצליל הוא שונה: במקום קובץ מסוג wav צריך להשתמש בקובץ מסוג mid. אפשר למצוא הרבה קבצי mid חופשיים לשימוש באתרים כמו פרוייקט מוטופיה או מוזיקה ברשות הכלל. כדאי לחפש שם משהו שימצא חן בעיניך.
- בחלון אפשרויות הפעולה של פעולת השמעת הצליל, לשנות את השדה loop ל-true כדי שאחרי שמוזיקה נגמרת היא תתחיל שוב מהתחלה.
את פעולת הצליל כדאי לשים בארוע תחילת המשחק של לוח הניקוד.
המוזיקה אמורה להתנגן ברקע בזמן המשחק.
יכול להיות שחלק מהמשתמשים לא ירצו לשמוע מוזיקה במהלך המשחק. כדאי לתת להם אפשרות להפסיק אותה:
- להוסיף ללוח הניקוד ארוע הקשה על מקש כלשהו, למשל M.
- לשים בו פעולה לבדיקה אם מתנגן צליל (שנמצאת בלשונית main1). בחלון אפשרויות הפעולה לבחור את הצליל של מוזיקת הרקע שבחרת.
- אם המוזיקה מתנגנת, צריך להפסיק אותה באמצעות הפעולה להפסקת צליל (שגם היא נמצאת בלשונית main1).
- להוסיף את פעולת בדיקת ההפך ("אחרת").
- אם לא מתנגנת מוזיקה, צריך להפעיל אותה באמצעות הפעולה להשמעת צליל.
עכשיו אפשר להפסיק את המוזיקה או להשמיע אותה שוב באמצעות לחיצה על מקש M.
אריזה
|
עכשיו תורך:
להוסיף למשחק הוראות שימוש, תמונת פתיחה ואייקון כמו שעשינו במשחק חייזרים. |
עכשיו אפשר ליצור תוכנה עצמאית ולשלוח לחברים!
קרידיט וויקיספר
|