לתכנת משחקים זה כיף, אם מבינים את המתמטיקה הבסיסית. פרויקט Byteroids! ממשיך עם מבוא לטרנספורמציות גאומטריות
בשבוע שעבר דיברנו בקצרה על ההיסטוריה של משחק הווידאו Asteroids, על כלליו ועל הפרויקטByteroids שיזמנו כאן לכתיבת משחק דומה. הנועזים שביקרו בדף הפרויקט במהלך השבוע בוודאי הבחינו בהסברים ובפתרונות שהוצעו שם, בינתיים, לשתי בעיות בוערות שרלוונטיות לכל משחק: תזמון מדויק, וקלט בו-זמני של מקשים שונים מהמקלדת. הפעם נדבר על הבסיס הגרפי של המשחק, ולמעשה של כל משחק דו-ממדי: טרנספורמציות גאומטריות במישור, או בשפת העם – להזיז, לסובב ולשנות את הגודל. מדובר בטכניקות בסיסיות למדי, אבל צריך להתחיל מאיפשהו, לא?
מתחילים בנקודהאנו מתחילים, כצפוי, בנקודה במישור. כל נקודה כזו כוללת שני ערכים – ערך בציר X וערך בציר Y. הערכים הללו יכולים לייצג קואורדינטות אמיתיות של פיקסל על המסך (או בחלון המשחק), אבל אין שום חובה לעשות זאת. למעשה, הקוד יהיה גמיש הרבה יותר אם נתרחק מהדרישות של הממשק הספציפי ונתכנן את הנקודות שלנו כך שיתאימו לכל חלון שהוא – למשל על ידי ייצוג ערכי X ו-Y באמצעות מספרים בין 0 ל-1. קל לתרגם מספרים כאלה למיקום על פני המסך, כאשר יודעים את הרזולוציה שלו, והתרגום יעבוד היטב בכל גודל מסך בו נבחר.
השלב הבא הוא קו שנמתח בין שתי נקודות, ואחריו מגיע המצולע שמורכב מקווים שונים, שכל אחד מהם מתחיל במקום בו קודמו נגמר. אם מותחים קו נוסף מסוף הקו האחרון לתחילת הראשון, "סוגרים" בכך את המצולע כולו ויוצרים צורה. זו יכולה להיות משולש (החללית שלנו), או מתומן עקום מעט (אסטרואיד).
הזזהאם החללית והאסטרואידים היו נשארים נייחים, המשחק היה משעמם מאד. על מנת לבצע תנועה, אנו מוסיפים לערך X של כל נקודה במצולע מספר קבוע מסוים, ומספר קבוע אחר לערך Y. התוספת הזו, מכיוון שהיא זהה עבור כל נקודה, גורמת להזזה של המצולע כולו במרחב. תוספת של ערך קבוע בציר X מזיזה את המצולע ימינה או שמאלה (מכיוון שמותר גם ערך שלילי), ואילו תוספת בציר Y מזיזה אותו למעלה או למטה. שילוב של השתיים הוא זה שיוצר תנועה אלכסונית.
פעולת ההזזה לבדה מספיקה עבור משחקי פלטפורמה קלאסיים, בהם הגיבור רץ, קופץ ונופל בין משטחים אופקיים. המשחק שלנו מורכב יותר ודורש התייחסות שונה לכל הנושא של כיוון.
![]()
סינוסים וקוסינוסים. יחשבו לכם כיוונים צילום : flickr, dichohecho, cc by
סיבוב
חללית
צילום : flickr, Yogi, cc by sa
אמרנו כיוונים – אמרנו מעלות. קוסינוסים וסינוסים. במשחקי חלליות דו-ממדיים בעלי גרפיקה מושקעת, המתכננים יוצרים מראש תמונות של החללית בכל זווית כך שאין צורך בחישובים כאלה כדי לצייר אותה. עם זאת, הם עדיין חיוניים ליצירה של תנועה אמינה. כבר אמרנו ששילוב של הזזה ב-X וב-Y יוצר תנועה אלכסונית. נניח שהשחקן ירה בכיוון של ארבעים מעלות: מה צריך להיות היחס בין ההזזה בציר X וההזזה בציר Y כדי שהקליע ינוע בפועל בארבעים מעלות? אותה שאלה בדיוק תקפה לגבי התנועה של החללית עצמה כאשר נפעיל את המנוע שלה.
דמיינו מחוג של שעון שמסתובב מול הפנים שלכם. הציבו מעל הראש מקור אור כך שהמחוג יטיל צל על המשטח שמתחתיו. מכיוון שהמחוג דק, הצל שלו הוא למעשה קו דו-ממדי. בעוד הוא מסתובב ממצב אופקי לאנכי, הצל הזה ילך ויתקצר לכיוון ציר הסיבוב, ולאחר מכן יתארך בכיוון השני. אורך הצל תלוי כמובן בזווית המחוג: למעשה, הוא שווה לאורך המחוג כפול קוסינוס הזווית. במילים אחרות, אם נחשוב על הסיטואציה כעל משולש ישר זווית שהמחוג הוא היתר שלו והצל הוא אחד הבסיסים, קוסינוס של זווית a מוגדר כאורך הצל חלקי אורך המחוג כאשר זווית המחוג היא a. כאשר a היא 0 מעלות, כלומר המחוג אופקי, קוסינוס הזווית הוא 1 – אורך הצל זהה לאורך המחוג. בזווית 90, הקוסינוס הוא 0 כי אורך הצל הוא אפס.
סינוס עובד באותו אופן, אלא שהפעם מקור האור בא מהצד והצל נופל על קיר במקום על הרצפה. באפס מעלות, הצל הזה של המחוג הוא אפס – ולכן סינוס הזווית הוא אפס. בתשעים מעלות הצל יהיה זהה למחוג – סינוס הזווית הוא 1. אינטואיטיבית אפשר לראות שהסינוס והקוסינוס משלימים זה את זה, וזו נקודה חשובה להמשך.
מעלות ורדיאניםהחישוב של הקוסינוס והסינוס, כלומר איך מגלים את היחס בין המחוג לצל בלי למדוד אותם בפועל, אינו טריוויאלי כלל. במקרים מסוימים אפשר לחשב אותו גאומטרית, אבל עבור רוב הזוויות מדובר בפיתוח מסובך שמעיק על המחשב עד כדי כך, שהמעבדים עצמם כוללים טבלאות מוכנות מראש שמסייעות להם לקבוע את הערך הנכון. הפונקציות Cos ו-Sin קיימות בכל שפת תכנות, אך לרוע המזל הן מוגדרות לרוב ברדיאנים ולא במעלות, ואם ניתן להם ערכים כמו 45 או 90 נקבל תוצאות משונות ביותר.
רדיאן הוא צורת מדידה אחרת של זווית, ביחידות של פיי (...3.1415). בהינתן שהיקפו של עיגול הוא שתיים כפול פיי כפול הרדיוס, ושהרדיוס בעצם לא משנה מבחינת הזווית, ניתן למדוד על ההיקף שני פיי רדיאנים. פיי רדיאנים מקבילים, אם כך, למאה ושמונים מעלות "רגילות" ואילו חצי פיי רדיאנים הם תשעים מעלות. אם אנו בוחרים, לצורך הנוחות שלנו, לעבוד במעלות, עלינו להמיר אותן בטרם החישוב לרדיאנים. זה נעשה על ידי הכפלתן בקבוע שערכו 360 חלקי שני פיי, או 180 חלקי פיי.
לא כל כך פשוט
כדי לסובב את החללית צריך נקודה בעלת מיקום מוגדר
צילום : flickr, Pentadact, cc by sa
אם יש לנו גודל מופשט כלשהו, נניח המהירות של קליע, קל להפוך אותו בחישובים כאלה לערכי X ו-Y שיניעו אותו על המסך. לדוגמה, אנו יורים קליע במהירות 10 וב-30 מעלות. קוסינוס של שלושים מעלות – אחרי התרגום לרדיאנים – הוא 0.866, נכפיל זאת בעשר ונקבל ערך קבוע ל-X של 8.66. הסינוס המתאים הוא 0.5, כלומר ערך Y של 5. אם נזיז את הקליע בכל צעד לפי ערכים אלה, נקבל תנועה בשלושים מעלות.
אבל, אם שמתם לב, לגודל הראשוני של המהירות לא היה כיוון. הוא היה "באוויר". לעומת זאת, כשאנו רוצים לסובב את המצולע שמרכיב את החללית עצמה, אנו מתחילים לא מגודל, שהוא מספר יחיד, אלא מנקודה שיש לה מיקום המוגדר על ידי שני מספרים נפרדים. תיאורטית, ניתן לחשב מתוך השניים האלה את הזווית ואת הגודל (היתר של המשולש), ואז להוסיף לזווית הזו את זווית הסיבוב a ולחשב מחדש את X ואת Y בהתאם. יש גם דרך מקוצרת ופשוטה יותר לעשות זאת. ראשית, נגדיר עבור כל נקודה את dx (המרחק שלה ממוקד הסיבוב, על ציר X) ואת dy (על ציר Y). אמרנו שהקוסינוס והסינוס משלימים, ולכן
NewX = dx * Cos(a) + dy * Sin(a)
NewY = -dx * Sin(a) + dy * Cos(a)
מדוע זה עובד, וכיצד עושים זאת בצורה חכמה יותר באמצעות כפל מטריצות – אלה נושאים שקצרה היריעה מלפרטם כאן, ולמעשה, אם נתכנת בחוכמה את המשחק שלנו, נוכל להתחמק מהם לגמרי.
שינוי גודלאם שרדתם עד כאן, שינוי הגודל ייראה לכם משחק ילדים. אנו נשתמש בו כשניקח את המצולעים שמגדירים את צורת האסטרואידים ונקטין אותם כדי ליצור את האסטרואידים הבינוניים והקטנים. גם שינוי גודל, כמו סיבוב, נעשה ביחס לנקודת מוקד כלשהי. אנו מחשבים שוב dx ו-dy, אך הפעם כל שעלינו לעשות הוא להכפיל אותם בפקטור שלנו. נניח שאחת הנקודות נמצאת ב-10,8 ואנו רוצים להקטין את המצולע כולו פי 2, עם מוקד בראשית הצירים. הפקטור הוא חצי, dx הוא 10 – כפול חצי נותן 5, ועבור dy נקבל 4. אם המוקד אינו בראשית הצירים, חשוב לזכור להוסיף את ה-X ואת ה-Y שלו לתוצאות כדי "להחזיר" את הנקודות למקומן הטבעי.
הפירוט הנ"ל היה תיאורטי למדי. כדי לראות כיצד הפעולות הללו מתבצעות בפועל ומשתלבות ביחד במשחק, עקבו אחרדף הפרויקט. עדכונים בקרוב!
[גיימר]



חללית 
ציטוט ההודעה