כיצד לתכנת משחקי מחשב?

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

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

חלק 1 מתוך 4: הכנת משחק מבוסס טקסט

  1. 1
    בחר שפת תכנות. כל שפות התכנות שונות, כך שתצטרך להחליט באיזו שאלה להשתמש כדי לכתוב את המשחק שלך. כל שפת תכנות מרכזית תומכת בקלט טקסט, בהפקת טקסט ובבניית if (הדברים העיקריים שאתה צריך עבור משחק מבוסס טקסט פשוט), כך שתוכל להחליט בעצמך. להלן מספר גורמים שיש לקחת בחשבון:
    • תחום היישום: שפות תכנות מסוימות, כמו JavaScript, מיועדות לשימוש באתרי אינטרנט, אחרות, כמו Python, C או C ++, מיועדות להפעלה במחשב. לשפות מסוימות יש מטרה ספציפית אחת, כמו R, המשמשת בעיקר לניתוח סטטיסטי. עבור המשחק שלך, עליך להשתמש בשפה עם שדה יישומים רחב יותר (Python, C, C ++, JavaScript, רבים אחרים).
    • קלות שימוש: למרות שכתיבת תוכנית צריכה להיות קלה דיה לאחר תרגול כלשהו בשפת תכנות רגילה (כלומר לא כזו שתוכננה במיוחד כדי לבלבל ולא להיות שמישה כמו Malbolge), חלקן ידידותיות למתחילים יותר מאחרות. Java ו- C, למשל, דורשים מהמתכנת להבין יותר מושגי תכנות מאשר Python. כמו כן, הודעות שגיאת פיתון קלות יותר להבין למתחילים מאשר, למשל, C הודעות שגיאה.
    • תאימות לפלטפורמה: כנראה שאתה רוצה שאנשים במערכות שונות, כגון לינוקס, מק או חלונות, יוכלו לשחק את המשחק שלך. לכן לא כדאי להשתמש בשפה הנתמכת רק בכמה מערכות, כמו למשל Visual Basic, הנתמכת רק ב- Windows. זו גם סיבה טובה לא לקודד את המשחק שלך ב- Assembler, שהוא ספציפי למערכת ולמעבד, וגם די קשה לתכנת איתו.

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

  2. 2
    התקן את הכלים הדרושים. אתה צריך משהו להרכיב או לפרש איתו את התוכנית, ואתה צריך משהו לכתוב / לערוך איתו. אם אתה רוצה ללכת בעקבות הדוגמה במאמר זה, אתה צריך להתקין פיתון ו ללמוד איך להפעיל תוכניות. אם אתה רוצה, אתה יכול להגדיר כמה אחד IDE או לשימוש Python מספק (זה נקרא IDLE), אבל אתה גם יכול פשוט להשתמש בעורך טקסט חביב כי טקסט תומך רגיל.
  3. 3
    הדפיסו טקסט. השחקן ירצה לדעת מה קורה ומה עליהם לעשות, אז כדאי שתדפיס עבורם טקסט.
    • זה נעשה עם print() הפונקציה בפייתון. כדי לנסות זאת, פתח קובץ חדש עם סיומת.py, הזן את הקוד הבא בתוכו, שמור והפעל אותו:
      הדפס ("ברוך הבא למשחק הניחושים למספרים!") הדפס ("הזן מספר שלם בין 1 ל 1000:") 
  4. 4
    הכניסו קצת אקראיות למשחק שלכם. אם שום דבר אינו אקראי, המשחק יהיה זהה לחלוטין בכל פעם, והשחקן ישתעמם במהירות.
    • במקרה זה, יש לבחור באופן אקראי מספר בתחילת התוכנית, כך שהנגן לא תמיד מנחש את אותו המספר. מכיוון שהוא אמור להישאר זהה לאורך כל התוכנית, עליך לאחסן את המספר האקראי במשתנה.
    • פייתון אינו מספק פונקציית מספר אקראי בליבה. אבל יש לה ספרייה סטנדרטית (זה אומר שהמשתמש לא יצטרך להתקין שום דבר נוסף) שעושה זאת. אז עבור לתחילת הקוד שלך (לפניהדפס()פונקציות) והקלד את השורה import random.
    • השתמש בפונקציה האקראית. זה נקראrandint (), נמצא ב אקראיהספרייה שאותה ייבאת, ולוקח את הערך המינימלי והמקסימלי שיכול להיות למספר כארגומנט. אז חזור לסוף הקוד והזן את השורה הבאה:
      rightNum = random.randint (0,1000) 
  5. 5
    קבל את קלט השחקן. במשחק, השחקן רוצה לעשות משהו או לקיים אינטראקציה עם משהו. במשחק מבוסס טקסט זה אפשרי על ידי הזנת טקסט.
    • מכיוון שהקוד שהזנת מדפיס את ההוראה להזין מספר לנגן, עליו לקרוא גם את המספר שהם מזינים. זה נעשה input() ב- Python 3 וב- raw_input() Python 2. כדאי לכתוב ב- Python 3, מכיוון שפייתון 2 יתיישן בקרוב. הוסף את השורה הבאה לקוד שלך כדי לאחסן את קלט הנגן במשתנה שנקראמספר:
      userNum = קלט () 
  6. 6
    הפוך את קלט המשתמש לסוג נתונים שמיש.
    • הפוך את קלט הנגן למספר. עכשיו זה אולי נראה מבלבל כי הם פשוט הזינו מספר. אבל יש סיבה טובה: פייתון מניח שכל הקלט הוא טקסט, או איך שהוא נקרא בתכנות, מחרוזת. והטקסט הזה מכיל את המספר שאתה רוצה לקבל. Python מספק פונקציה להמרת מחרוזת המכילה רק מספר למספר שבתוכו. סוג:
      userNum = int (userNum) 
  7. 7
    עיבוד קלט המשתמש. זה יהיה חסר טעם פשוט לבקש מהשחקן להזין דברים אקראיים. אתה צריך לעשות משהו עם המידע שהמשתמש הזין.
    • השווה את מספר המשתמש למספר הנכון. המספרים אמנם לא זהים, אך זה אמור לגרום למשתמש להזין מספר אחר. כאשר המספרים תואמים, הוא אמור להפסיק לקבל קלט חדש, לומר למשתמש שהוא ניחש נכון, ולצאת מהתוכנית. זה נעשה עם הקוד הבא:
      בעוד userNum! = rightNum: userNum = int (קלט ()) 
  8. 8
    תן למשוב לשחקן משוב. אמנם כבר עיבדת את הקלט, אך המשתמש אינו יכול לראות זאת. עליכם להדפיס את התוצאה בפועל למשתמש כדי שיבינו מה קורה.
    • אין ספק שאתה יכול פשוט לומר למשתמש אם המספר שלהם נכון או לא נכון. אבל עם גישה זו, השחקן יצטרך לנחש 1000 פעמים במקרה הגרוע ביותר, וזה יהיה משעמם מאוד.
    • אז ספרו לשחקן האם מספרם קטן מדי או גדול מדי. זה יקטין משמעותית את מספר הניחושים: אם, למשל, המשתמש מנחש 500 ראשונים, ואומרים לו שהוא גדול מדי, יש עכשיו רק 500 מספרים אפשריים במקום 1000. זה נעשה עם קונסטרוקציות אם, אז החלף אתהדפס ("לא נכון. נסה שוב.") עם אחד.
    • שים לב שבדיקה אם שני מספרים זהים נעשית עם ==, ולא עם =. = מקצה את הערך הימני שלו למשתנה השמאלי ממנו!
    • אם userNum <rightNum: הדפס ("קטן מדי. נסה שוב:") אם userNum> rightNum: הדפס ("גדול מדי. נסה שוב:") 
  9. 9
    בדוק את הקוד שלך. בתור מתכנת, אתה צריך להיות בטוח שהקוד שלך עובד בכל המקרים לפני שאתה שוקל שהוא הושלם.
    • בעת תכנות בפייתון, וודא שאתה מקבל את הכניסה נכונה. הקוד שלך צריך להיראות כך:
      ייבא הדפסה אקראית ("ברוך הבא למשחק ניחוש המספרים!") הדפס ("הזן מספר שלם בין 1 ל 1000:") rightNum = random.randint (0,1000) userNum = input () userNum = int (userNum) תוך userNum! = rightNum: אם userNum <rightNum: הדפס ("קטן מדי. נסה שוב:") אם userNum> rightNum: הדפס ("גדול מדי. נסה שוב:") userNum = int (קלט ()) הדפס ("ניחשת נכון) נכונה.") 
  10. 10
    אמת את הקלט. משתמש לא אמור להיות מסוגל לשבור את התוכנית שלך בפעולות פשוטות כמו הזנת דבר לא נכון. אימות הקלט פירושו האם המשתמש הזין את הדבר הנכון לפני עיבודו.
    • פתח שוב את המשחק ונסה להזין כל דבר שאינו מספר. המשחק ייצא עםValueError. כדי להימנע מכך, עליך ליישם דרך לבדוק אם הקלט היה מספר.
    • הגדר פונקציה. מכיוון שאימות הקלט ארוך למדי, ועליך לעשות זאת מספר פעמים, עליך להגדיר פונקציה. זה לא יידרש ויכוחים ויחזיר מספר. ראשית, כתוב def numInput(): בחלק העליון של הקוד שלך, ישירות מתחת ל-ייבוא אקראי.
    • קבל את קלט המשתמש פעם אחת. השתמש input() בפונקציה והקצה את התוצאה למשתנה inp.
    • אמנם קלט המשתמש אינו מספר, בקש ממנו להזין מספר. בדיקה אם מחרוזת היא מספר נעשית באמצעות isdigit() הפונקציות, המאפשרת רק מספר שלם, כך שלא תצטרכו לבדוק זאת בנפרד.
    • כאשר הקלט הוא מספר, המירו אותו ממחרוזת למספר והחזירו את התוצאה. השתמש int() בפונקציה להמרת המחרוזת למספר שלם. זה יהפוך את ההמרה בקוד הראשי למיותרת, וכדאי להסיר אותה משם.
    • החלף את כל השיחות אל קלט() בקוד הראשי עם שיחות אל מספר קלט ().
    • הקוד של מספר קלט () הפונקציה תיראה כך:
    • def numInput (): inp = קלט () בזמן שאינו inp.isdigit (): הדפס ("נאמר לך להזין מספר שלם! הזן מספר שלם:") inp = input () return int (inp) 
  11. 11
    בדוק את המשחק שוב. שימו לב במיוחד אם אימות הקלט שלכם עובד על ידי הזנת משהו לא בסדר בכוונה.
    • נסה להזין טקסט כלשהו כאשר התוכנית מבקשת ממך מספר. כעת, במקום לצאת עם הודעת שגיאה, התוכנית תבקש ממך שוב מספר.
  12. 12
    הצע להפעיל מחדש את המשחק לאחר סיומו. בדרך זו, השחקן יכול לשחק את המשחק שלך זמן רב יותר מבלי להצטרף מחדש כל הזמן.
    • שים את כל הקוד למעט הייבוא והגדרת הפונקציה לולאת זמן. הגדר True כתנאי: זה תמיד יהיה נכון, כך שהלולאה תמשיך לנצח.
    • שאל את השחקן אם הוא רוצה לשחק שוב לאחר שניחש את המספר נכון. השתמש print() בפונקציה.
    • אם הם עונים "לא", צא מהמראה. אם הם עונים למשהו אחר, המשך. פריצת לולאה נעשית break בהצהרה.
    • העבר את "ברוך הבא למשחק הניחוש למספרים" מחוץ לולאת הזמן. השחקן כנראה לא רוצה להתקבל בברכה בכל פעם שהם משחקים את המשחק. העבירו את ההוראההדפס ("ברוך הבא למשחק הניחושים למספרים!" מעל ה בעוד נכון:, כך שהוא יודפס פעם אחת בלבד, כאשר המשתמש יתחיל את המשחק הראשון.
  13. 13
    בדקו את המשחק. עליכם להיות בטוחים שהמשחק שלכם עדיין עובד לאחר הטמעת תכונות חדשות.
    • הקפד לענות על "כן" ועל "לא" לפחות פעם אחת כדי לוודא ששתי האפשרויות עובדות. כך ייראה הקוד שלך:
      יבוא def numInput אקראי (): inp = קלט () ואילו לא inp.isdigit (): הדפס ("נאמר לך להזין מספר שלם! הזן מספר שלם:") inp = קלט () החזר int (inp) הדפס ("ברוך הבא למשחק ניחוש המספרים!") בעוד נכון: הדפס ("הזן מספר שלם בין 1 ל 1000:") rightNum = random.randint (0,1000) userNum = numInput () בעוד userNum! = RightNum: if userNum <rightNum: הדפס ("קטן מדי. נסה שוב:") אם userNum> rightNum: הדפס ("גדול מדי. נסה שוב:") userNum = numInput () הדפס ("ניחשת נכון.") הדפס ("האם אתה רוצה לשחק שוב? הזן לא כדי להפסיק. ") אם קלט () ==" לא ": הפסקה 
  14. 14
    כתוב משחקים אחרים מבוססי טקסט. מה דעתך לכתוב בהמשך הרפתקה טקסטית? או משחק חידון? תהיה יצירתי.

    טיפ: לפעמים מועיל לחפש בתיעוד אם אינך בטוח כיצד נעשה משהו או כיצד משתמשים בפונקציה. תיעוד ה- Python 3 נמצא בכתובת https://docs.python.org/3/. לפעמים חיפוש אחר כל מה שתרצה לעשות באינטרנט גם מחזיר תוצאות טובות.

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

חלק 2 מתוך 4: הכנת משחק עם גרפיקה דו-ממדית

  1. 1
    בחר ספרייה. יצירת גרפיקה מסובכת מאוד, ורוב שפות התכנות (כולל פייתון, C ++, C, JavaScript) מספקות תמיכה מינימלית בלבד ואף לא בגרפיקה בליבה או בספריות הרגילות. אז תצטרכו להשתמש בספרייה חיצונית כדי שתוכלו ליצור גרפיקה, למשל Pygame for Python.
    • גם עם ספריית גרפיקה תצטרך לדאוג הרבה לדברים ברמה נמוכה כמו כיצד להציג תפריט, כיצד לבדוק אם המשתמש לחץ עליו, כיצד להציג את האריחים וכן הלאה. אם אתה מעדיף להתמקד בפיתוח המשחק בפועל, ובמיוחד אם המשחק שאתה רוצה לעשות הוא מורכב, עליך להשתמש בספריית מנועי משחק, המיישמת דברים כאלה.

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

  2. 2
    התקן את הספרייה שבחרת. Cocos2D עבור Python מותקן פשוט עם sudo pip3 install cocos2d.
  3. 3
    הכינו ספריה חדשה. תוכלו להשתמש בדברים כמו תמונות וצלילים במשחק שלכם. אתה צריך לשמור את הדברים האלה באותה מדריך כמו התוכנית, והספריה לא צריכה להכיל שום דבר אחר כדי שתוכל לראות בקלות אילו נכסים יש לך במשחק.
  4. 4
    עבור לספרייה החדשה וצור קובץ קוד חדש. זה צריך להיות שם ראשי, עם סיומת הקובץ של שפת התכנות שלך. אם אתה כותב תוכנית גדולה ומורכבת שבה הגיוני שיש מספר קבצי תוכנה, זה יראה איזו מהן היא העיקרית.
    • בדוגמה זו, קובץ זה, אשר צריך להיקרא main.py, יכיל את כל הקוד שלך. אך הספרייה שיצרת עדיין תהיה שימושית עבור קבצי מדיה אחרים.
  5. 5
    הכינו חלון. זהו התנאי הבסיסי למשחק עם גרפיקה. תוכלו להוסיף את התוכן הפשוט ביותר כעת, כמו למשל צבע רקע.
    • ייבא את מודולי המשנה הדרושים ל- cocos2d: קוקוס.במאי, קוקוס. סצנה ו שכבה קוקוס. זה נעשה עם from subModuleName import *, כאשר subModuleName הוא מודול המשנה שברצונך לייבא. ההבדל ביןמ... יבוא * ו יבוא... זה שאתה לא צריך להציב את שם המודול מול כל מה שאתה משתמש מאותו מודול עם הקודם.
    • הגדר תת-מחלקה MainMenuBgr של ה-ColorLayer. זה בעצם אומר שכל רקע תפריט ראשי שתיצור יתנהג כמו שכבת צבע עם כמה שינויים שתבצע.
    • אתחל את מנהל הקוקוס. זה ייתן לך חלון חדש. אם לא תגדיר כיתוב כלשהו, בחלון תהיה כיתוב זהה לזה של שם הקובץ (main.py), שנראה לא מקצועי במיוחד. אפשר לשנות את גודל החלון באמצעות הגדרהניתן לשנות גודל ל נכון.
    • הגדר פונקציה showMainMenu. עליכם להכניס את הקוד להצגת התפריט הראשי לפונקציה מכיוון שהדבר יאפשר לכם לחזור לתפריט הראשי בקלות על ידי קריאה לפונקציה שוב.
    • צור סצנה. הסצנה מורכבת משכבה אחת לעת עתה, שהיא אובייקט שלMainMenuBgr כיתה שהגדרת.
    • הפעל את הסצנה הזו בחלון.
    • מייבוא cocos.director * מ cocos.scene ייבוא * מ cocos.layer ייבוא * מחלקה MainMenuBgr (ColorLayer): def _init_ (עצמי): סופר (MainMenu, עצמי)._ init_ (0,200,255,255) def showMainMenu (): menuSc = Scene (MainMenuBgr ()) director.run (menuSc) director.init (caption = "IcyPlat - platformer פשוט", resizable = True) showMainMenu () 
  6. 6
    צור תפריט ראשי. מלבד המשחק בפועל, תהיה לך אפשרות לסגור את המשחק. תוכלו להוסיף קרדיטים בהמשך, אך להתעלם מהם לעת עתה. תפריט ראשי ימנע מהכניסה למשחק בלתי צפויה מדי.
    • ייבא מודולים נחוצים. אתה צריך לייבאתפריט קוקוס (שוב עם מ הוראה) ו פיגלט.אפ (הפעם עם יבוא).
    • הגדר את MainMenu ככיתת משנה של תפריט.
    • הגדר את היישור של התפריט הראשי. עליך להגדיר את היישור האנכי והאופקי בנפרד.
    • צור רשימה של פריטי תפריט והוסף צור את התפריט בפועל. אתה צריך לקבל את פריטי התפריט "התחל משחק" ו"צא ". הקפד לשים כל פריט בתפריט שנוצר בין סוגריים. לפריט תפריט יש תווית ופונקציה להתקשרות חוזרת עבור לחיצה עליו. עבור הפריט "התחל משחק" השתמש startGame בפונקציה (תכתוב אותו בקרוב), עבור הפריט "צא" השתמש ב" pyglet.app.exit "(כבר קיים). צור את התפריט בפועל על ידי התקשרות self.create_menu(menuItems).
    • הגדר startGame(). רק הכנס pass להגדרה לעת עתה, תחליף את זה כשאתה כותב את המשחק בפועל.
    • עבור למקום בקוד שבו יצרת את ה- menuSc סצנה, והוסף אליו אובייקט MainMenu.
    • כל הקוד שלך אמור להיראות כך:
      מייבוא cocos.director * מ cocos.menu ייבוא * מ cocos.scene ייבוא * מ cocos.layer ייבוא * ייבוא מחלקה pyglet.app MainMenuBgr (ColorLayer): def _init_ (עצמי): סופר (MainMenuBgr, עצמי)._ init_ (0,200,255,255) class MainMenu (Menu): def _init_ (self): super (MainMenu, self)._ init_ ("") self.menu_valign = CENTER self.menu_halign = CENTER menuItems = [(MenuItem ("התחל משחק", startGame)), (MenuItem ("צא", pyglet.app.exit))] self.create_menu (menuItems) def startGame (): לעבור def showMainMenu (): menuSc = Scene (MainMenuBgr ()) menuSc.add (MainMenu ()) במאי. הפעל (menuSc) director.init (caption = "IcyPlat - פלטפורמה פשוטה", resizable = True) showMainMenu () 
  7. 7
    בדוק את הקוד שלך. חשוב לבדוק את הקוד שלך בשלב כה מוקדם, בעוד שהוא עדיין קצר ופשוט יחסית: כך תדע שגיאות במבנה הבסיסי, ותוכל לתקן אותן לפני שהן יגרמו לבעיות נוספות.
    • הקוד מההוראות צריך לפתוח חלון עם הכיתוב "IcyPlat - פלטפורמה פשוטה", שניתן לשנות את הגודל ושיש לו רקע תכלת. עליו להיות תפריט עם שני פריטים: כאשר אתה לוחץ על "התחל משחק", שום דבר לא קורה; כאשר אתה לוחץ על "צא", התוכנית מסתיימת.
  8. 8
    הצג ספרייט במשחק. הספרייט הוא כמו "אובייקט משחק". בפלטפורמה למשל, צריך להיות הדמות העיקרית שהשחקן יכול לשלוט בה (בשלב זה היא תוצג רק עם זאת). דברים כמו קישוטי רקע או אובייקט שהנגן יכול לקיים איתם אינטראקציה יכולים להיות גם ספריטים, אך עליכם להוסיף רק תחילה כדי להבין את הרעיון, ואז תוכלו להוסיף כל מה שתרצו.
    • ייבא את cocos.sprite תת מודול עם הביטוי מ- יבוא.
    • מצא תמונה. אינך יכול להציג ספרייט אם אין לך תמונה בשבילו. אתה יכול לצייר אחד כזה, או שאתה יכול להשיג אחד מהאינטרנט (אם כי אתה מתכנן לפרסם את המשחק שלך, היזהר מהרישיון), למשל מכאן (חתוך את התמונה כך שיהיה לך רק פינגווין אחד פועל). הקפד להכניס את התמונה לאותה מדריך כמו התוכנית.
    • צור את שכבת הספרייט ואת הספרייט. צור את השכבה כאובייקט חדש של ה-ScrollableLayerמעמד. צור את הספרייט כמושדוןהתנגד והגדר את מיקומו ל- (8, 250). לצורך התייחסות, הנקודה (0, 0) נמצאת בפינה השמאלית התחתונה. זה די גבוה, אבל הוא יוודא שהפינגווין לא יתקע בקרח.
    • מוסיפים את הספרייט לשכבת הספרייט.
    • צור סצנה חדשה מתוך שכבת הספרייט והפעל אותה.
    • def startGame (): figLayer = ScrollableLayer () fig = Sprite ('pingu.png') fig.position = (75, 100) figLayer.add (fig) # gameSc = Scene (figLayer) director.run (gameSc) 
    • אתה יכול להריץ את הקוד שלך עכשיו אם תרצה. תראה דמות פינגווין קטנה (או כל מה שציירת) על רקע שחור לאחר שתלחץ על "התחל משחק".
  9. 9
    החליטו ממה יהיה מורכב הנוף שלכם. ברוב המשחקים, הספריטים שלך לא צריכים פשוט לצוף בריק. הם אמורים לעמוד על משטח כלשהו, עם משהו סביבם. במשחקי דו מימד, זה נעשה לעתים קרובות עם ערכת אריחים ומפת אריחים. סט האריח בעצם אומר איזה סוג של ריבועי שטח ריבועים רקע להתקיים, ועל איך הם נראים.
    • צור ערכת אריחים. ערכת האריחים למשחק זה תהיה בסיסית מאוד: אריח אחד לקרח ואריח אחד לשמיים. אריח הקרח המשמש בדוגמה זו הוא מכאן, תחת CC-BY-SA 3,0.
    • צור תמונת ערכת אריחים. זו תמונה של כל האריחים, שכולם צריכים להיות באותו גודל (ערוך אותם אם לא) ויש להם את הגודל שאתה רוצה לראות במשחק, אחד ליד השני. שמור את התמונה שלך בתור icyTiles.png.
    • צור את תיאור מערך האריחים. זהו קובץ XML. קובץ ה- XML מכיל מידע על גודל האריחים בתמונת ערכת האריחים, באיזו תמונה להשתמש והיכן למצוא איזה אריח שם. צור קובץ XML בשם icyTiles.xml עם הקוד למטה:
      <? xml version = "1,0"?> <resource> <imageatlas size = "16x16" file = "icyTiles.png"> <image id = "i-ice" offset = "00" /> <image id = "i-sky" אופסט = "160" /> </imageatlas> <tileset> <tile id = "ice"> <image ref = "i-ice" /> </tile> <tile id = "sky"> <image ref = "i-sky" /> </tile> </tileset> </resource> 
  10. 10
    הפוך מבנה ממשי אלמנטים של הנוף שלך. אם הכנתם ערכת אריחים, יש לעשות זאת בצורה של מפת אריחים. מפת אריחים היא כמו מפה המגדירה איזה אריח נמצא במיקום ברמה שלך. בדוגמה, עליכם להגדיר פונקציה ליצירת מפות אריחים מכיוון שתכנון מפות אריחים ביד הוא מייגע מאוד. למשחק מתקדם יותר בדרך כלל יהיה עורך ברמה כלשהו, אך כדי להכיר את פיתוח המשחק הדו-ממדי, אלגוריתם יכול לספק רמות מספיק טובות.
    • גלה כמה שורות ועמודות נדרשים. לשם כך, חלק את גודל המסך בגודל האריח הן אופקית (עמודות) והן אנכית (שורות). לעגל את המספר כלפי מעלה; אתה צריך פונקציה של מודול המתמטיקה בשביל זה, אז הוסף לייבוא בחלק העליון של הקוד שלך.from math import ceil
    • פתח קובץ לכתיבה. פעולה זו תמחק את כל התוכן הקודם של הקובץ, לכן בחר שם שאף קובץ בספרייה עדיין לא כולל levelMap.xml.
    • כתוב את תגי הפתיחה לקובץ.
    • צור מפת אריחים בהתאם לאלגוריתם. אתה משתמש בזה שבקוד שלמטה, או שאתה יכול לבוא עם אחד לבד. הקפד לייבא אתrandint פונקציה מהמודול אקראי: זה נדרש כדי שהקוד שלמטה יעבוד, וכל מה שתעלה עליו כנראה יצטרך גם מספרים שלמים אקראיים. כמו כן, דאגו לשים אריחי שמיים ואריחי קרח בשכבות שונות: קרח הוא מוצק, שמיים לא.
    • כתוב את תגי הסגירה לקובץ וסגור את הקובץ.
    • def generateTilemap (): colAmount = ceil (800/16) * 3 # (רוחב מסך / גודל אריח) * 3 rowAmount = ceil (600/16) # גובה מסך / אריח אריח FileFile = פתוח ("levelMap.xml", " w ") tileFile.write ('<resource> \ n <דורש file =" icyTiles.xml "/> \ n <rectmap id =" solid "origin =" 00,1" tile_size =" 16x16" > \ n') iceHeight = randint (1,10) עבור i בטווח (0, colAmount): tileFile.write ('<column>') makeHole = False if randint (0,50) == 10 ו- i! = 0: # don ' t לאפשר חורים בנקודה spawn makeHole = נכון עבור j בטווח (0, rowAmount): אם makeHole: tileFile.write ('<תא /> \ n') אחר: אם j <= iceHeight: tileFile.write ('<תא tile = "ice" /> \ n ') אחר: tileFile.write (' <cell /> \ n ') iceHeight = randint (iceHeight-5,iceHeight + 5) אם iceHeight <0: # מגביל את האריחים מלהגיע נמוך מדי IceHeight = randint (1,5) אם iceHeight> rowAmount: # מגביל את האריחים מ- IceHeight גבוה מדי = Randint (int (rowAmount / 2) -5, int (rowAmount / 2) +5) tileFile.write ('</column> \ n') tileFile.write ('</rectmap> \ n <rectmap id = "not_solid" origin = "00,0" tile_size = "16x16" > \ n ') עבור i בטווח (0, colAmount): tileFile.write (' <column> ') עבור j בטווח (0, rowAmount): tileFile.write (' <cell tile =" sky "/> \ n ') tileFile.write (' </column> \ n ') tileFile.write (' </rectmap> \ n </resource> \ n ') tileFile.close ()# הגבלת אריחים מלהגיע לקרח גבוה מדי גובה = randint (int (rowAmount / 2) -5, int (rowAmount / 2) +5) tileFile.write ('</column> \ n') tileFile.write ('</ rectmap > \ n <rectmap id = "not_solid" origin = "00,0" tile_size = "16x16"> \ n ') עבור i בטווח (0, colAmount): tileFile.write (' <column> ') עבור j ב range (0, rowAmount): tileFile.write ('<cell tile = "sky" /> \ n') tileFile.write ('</column> \ n') tileFile.write ('</rectmap> \ n < / resource> \ n ') tileFile.close ()# הגבלת אריחים מלהגיע לקרח גבוה מדי גובה = randint (int (rowAmount / 2) -5, int (rowAmount / 2) +5) tileFile.write ('</column> \ n') tileFile.write ('</ rectmap > \ n <rectmap id = "not_solid" origin = "00,0" tile_size = "16x16"> \ n ') עבור i בטווח (0, colAmount): tileFile.write (' <column> ') עבור j ב range (0, rowAmount): tileFile.write ('<cell tile = "sky" /> \ n') tileFile.write ('</column> \ n') tileFile.write ('</rectmap> \ n < / resource> \ n ') tileFile.close ()) עבור j בטווח (0, rowAmount): tileFile.write ('<cell tile = "sky" /> \ n') tileFile.write ('</column> \ n') tileFile.write ('</ rectmap > \ n </resource> \ n ') tileFile.close ()) עבור j בטווח (0, rowAmount): tileFile.write ('<cell tile = "sky" /> \ n') tileFile.write ('</column> \ n') tileFile.write ('</ rectmap > \ n </resource> \ n ') tileFile.close () 
  11. 11
    הצג את מפת האריחים. ייבא הכל מ- cocos.tiles ואז נכנס ל- התחל משחק פונקציה לשם כך.
    • בתחילת שלך התחל משחק פונקציה, צור מפת אריחים באמצעות הפונקציה שהגדרת לשם כך.
    • צור מנהל גלילה חדש. עשה זאת ישירות מתחת לקו שבו אתה מוסיף את הספרייט לשכבתו.
    • צור שכבה חדשה המכילה את האריחים, שתוטען מה- levelMap.xml מפה אריחים שלך createTilemap פונקציה שנוצרה.
    • הוסף את השכבה הלא מוצקה, את השכבה המוצקה ואת שכבת הספרייט למנהל הגלילה, בדיוק לפי הסדר הזה. אתה יכול להוסיף מיקום z אם תרצה.
    • במקום ליצור את הסצנה משכבת הספרייט, צור אותה ממנהל הגלילה.
    • שלך התחל משחק הפונקציה אמורה להיראות כך:
      def startGame (): createTilemap () # fig = Sprite ('pingu.png') fig.position = (8, 500) figLayer = ScrollableLayer () figLayer.add (fig) # tileLayer = load ('levelMap.xml') solidTiles = tileLayer ['solid'] nsoliTiles = tileLayer ['not_solid'] # scrMang = ScrollingManager () scrMang.add (nsoliTiles, z = -1) scrMang.add (solidTiles, z = 0) scrMang.add (figLayer, z = 1) game.cc = Scene (scrMang) director.run (gameSc) 
  12. 12
    בדוק את הקוד שלך. עליך לבדוק את הקוד שלך לעתים קרובות כדי לוודא שהתכונות החדשות שהטמעת באמת פועלות.
    • הקוד בדוגמה אמור להראות כעת איזה נוף קרח מאחורי הפינגווין. אם הפינגווין נראה כאילו הוא מרחף הרחק מעל הקרח, לא עשית שום דבר רע, והוא יתוקן בשלב הבא.
  13. 13
    הוסף את הפקדים. לשחקן יש הרבה יותר דרכים לקיים אינטראקציה עם התוכנית במשחק דו-ממדי מאשר במשחק מבוסס טקסט. אחד נפוץ כולל העברת הדמות שלהם כאשר לוחצים על המקש הנכון.
    • ייבא הכל ממנה cocos.mapcolliders וממנה cocos.actions. ייבא גם key מ pyglet.window.
    • "הכריז" על כמה משתנים גלובליים. משתנים גלובליים משותפים בין פונקציות. אתה לא באמת יכול להכריז על משתנים בפייתון, אבל אתה צריך לומר שמשתנה גלובלי קיים בקוד הראשי לפני השימוש בו. ניתן להקצות 0 כערך מכיוון שפונקציה תדאג להקצות את הערך הנכון בהמשך. אז הוסף תחת ביטויי הייבוא:
      # "הצהרה" על משתנים גלובליים מקלדת = 0 scrMang = 0 
    • התאם את התחל משחק פונקציה:
      • תגיד שאתה משתמש במשתנים הגלובליים מקלדת ו scrMang. עשו זאת על ידי כתיבה global keyboard, scrMang בראש הפונקציה.
      • גרמו לחלון להקשיב לאירועי המקלדת.
      • אמור לדמות לפעול על פי א PlatformerController. אתה תיישם את זהPlatformerController בקרוב.
      • צור מתווך מפות לטפל בהתנגשויות בין האריחים המוצקים לדמות.
      def startGame (): מקלדת גלובלית, scrMang createTilemap () # fig = Sprite ('pingu.png') fig.position = (8, 250) figLayer = ScrollableLayer () figLayer.add (fig) # tileLayer = load ('levelMap.xml ') solidTiles = tileLayer [' solid '] nsoliTiles = tileLayer [' not_solid '] # keyboard = key.KeyStateHandler () director.window.push_handlers (keyboard) # fig.do (PlatformerController ()) mapcollider = RectMapCollider (velocity_on_bump = 'שקופית') fig.collision_handler = make_collision_handler (mapcollider, solidTiles) # scrMang = ScrollingManager () scrMang.add (nsoliTiles, z = -1) scrMang.add (solidTiles, z = 0) scrMang.add (figLayer, z = 1) # gameSc = Scene (scrMang) director.run (gameSc) 
    • צור בקר פלטפורמה. זה מה שיזיז את הדמות בהתאם ללחיצות המקשים שלך.
      • הגדר את בקר הפלטפורמה כתת-מחלקה של פעולה.
      • הגדירו את מהירות התנועה, מהירות הקפיצה וכוח המשיכה.
      • תגדיר את התחלהפונקציה. פונקציה זו נקראת פעם אחת, כאשר בקר הפלטפורמה מחובר לדמות. עליו להגדיר את מהירותו ל- 0 הן ב- x והן בכיוון y.
      • תגדיר את שלבפונקציה. זה יחזור על עצמו בזמן שהסצנה פועלת.
      • תגיד ל שלב פונקציה לשימוש במשתנים הגלובליים מקלדת ו scrMang.
      • קבלו ושינו את המהירות. שמור את ה- x ואת המהירות y במשתנים נפרדים. הגדר את מהירות ה- x ל- 1 או -1 (תלוי אם לחצו על המקש השמאלי או הימני) כפול מהירות התנועה. הוסף כוח משיכה למהירות y. הכפל אותו עם זמן השבתה כך שהוא עובד באותה צורה במכשירים איטיים יותר. אם לוחצים על מקש הרווח והדמות עומדת על הקרקע, קפצו על ידי שינוי מהירות y למהירות הקפיצה.
      • חשב לאן הדמות צריכה לנוע. ואז תן למטפל ההתנגשות להתאים את המיקום הזה אם הוא נמצא בתוך אריח מלא. לבסוף, העבירו את הדמות למיקום המותאם החדש.
      • הגדר את המיקוד של מנהל הגלילה על הדמות. זה גורם למצלמה לנוע בצורה סבירה כשהדמות זזה.
      Class PlatformerController (פעולה): מקלדת גלובלית, scrMang on_ground = True MOVE_SPEED = 300 JUMP_SPEED = 500 GRAVITY = -1200 def התחלה (עצמי): self.target.velocity = (0, 0) שלב def (עצמי, dt): מקלדת גלובלית, גלילה אם dt> 0,1: # אל תעשו שום דבר בזמן השבתה להחזר גדול vx, vy = self.target.velocity vx = (מקלדת [key.RIGHT] - מקלדת [key.LEFT]) * self.MOVE_SPEED vy + = self.GRAVITY * dt אם self.on_ground ומקלדת [key.SPACE]: vy = self.JUMP_SPEED dx = vx * dt dy = vy * dt last = self.target.get_rect () new = last.copy () new.x + = dx new.y + = dy self.target.velocity = self.target.collision_handler (last, new, vx, vy) self.on_ground = (new.y == last.y) self.target.position = new.center scrMang.set_focus (* new.center) 
  14. 14
    בדוק את הקוד שלך. אם עקבת אחר הדוגמה, כעת תוכל להיות מסוגל להזיז את הפינגווין באמצעות מקשי החצים ולקפוץ על ידי לחיצה על מקש הרווח. כמו כן, כעת על הפינגווין ליפול למטה במקום לרחף מעל פני האדמה.
  15. 15
    תן למשחק להסתיים. גם למשחקים שיכולים להימשך בלי סוף יש אפשרות לסיים אותם בהפסד. ומכיוון שלרמה שהצבת בדוגמה עם פונקציה יש סוף, זה אמור להיות אפשרי גם לנצח על ידי הגעה למטרה זו: אחרת, השחקן היה קופץ רק על גושי הקרח שם, שמשעממים אחרי זמן מה..
    • בתוך בקר הפלטפורמה, לאחר הגדרת המיקוד, השג את מיקום ה- X וה- Y של הדמות. אם מיקום y קטן מ- 0, קרא לפונקציה finishGame()(תכתוב אותה בהמשך) "Game Over" כארגומנט. אם מיקום ה- x גדול מגודל המסך מוכפל עם 3 (הגדרת זאת כגודל רמה בעבר).
      posX, posY = self.target.position if posY <0: finishGame ("Game Over") החזר אם posX> 800 * 3: # רמת level finishGame ("רמה הושלמה") החזרה 
    • הגדר כיתה גימור תפריט. זה צריך להיות כמו מחלקת התפריט הראשי שהגדרת בעבר, אך במקום שיהיה לו מחרוזת ריקה ככותרת, עליו להשתמש במשתנהטקסט ש ה _init_פונקציה לוקחת כוויכוח. יש לסמן את פריטי התפריט "נסה שוב" ו"צא "כעת, אך הפונקציות שהם מכנים נשארות זהות.
      class FinishMenu (Menu): def _init_ (self, text): super (FinishMenu, self)._ init_ (text) self.menu_valign = CENTER self.menu_halign = CENTER menuItems = [(MenuItem ("נסה שוב", startGame)), (MenuItem ("צא", pyglet.app.exit))] self.create_menu (menuItems) 
    • הגדר את הפונקציה finishGame (). זה צריך לקחתטקסטכוויכוח. זה אמור להפוך סצנה מרקע התפריט הראשי, אFinishMenu עם ה טקסטהוויכוח מועבר לתפריט זה. ואז זה צריך להריץ את הסצנה הזו.
      def finishGame (טקסט): menuSc = סצנה (MainMenuBgr ()) menuSc.add (FinishMenu (טקסט)) director.run (menuSc) 
  16. 16
    הוסף נקודות זכות. זה בדרך כלל הכרחי אם השתמשת במדיה שלא עשית בעצמך לזכות את מחברם. כמו כן, סביר להניח שתרצו שהשחקנים יידעו שכתבתם את המשחק. יש לכלול את הזיכויים במשחק, כך שכל מי שמתקין את המשחק שלך יוכל לראות אותם מבלי שיידרש לעיין עמוק בקבצי המשחק.
    • צור קובץ CREDITS והזן שם את כל הזיכויים שלך, כך:
      פינגווין: קלווין שאדינג, תחת CC0 גוש קרח: Michał Banas digit1024 ב- opengameart.org תחת CC-BY-SA 3.0 
    • חזור לקוד ה- Python שלך וייבא Label מ- cocos.text.
    • הגדר תת-מחלקה נקודות זכות של שכבה. ב_init_ פונקציה, קרא את נקודות זכות הקובץ והכין תווית טקסט במיקום הנכון מתוך כל שורה בו.
      נקודות זיכוי (שכבה): def _init_ (עצמי): סופר (קרדיטים, עצמי)._ init_ () credFile = open ("CREDITS", "r") creds = credFile.read () creds = creds.split ("\ n ") עבור i בטווח (0, len (creds)): credLabel = תווית (creds [i], font_size = 32, anchor_x =" left ", anchor_y =" top ") credLabel.position = 25,500- (i + 1) * 40 self.add (credLabel) 
    • עבור לשיעור התפריט הראשי שלך והוסף פריט בתפריט שכותרתו "קרדיטים" המכנה את הפונקציהshowCredits כאשר לוחצים עליו.
    • הגדר תת-מחלקה BackToMainMenuButton של תפריט. הפוך את זה לתפריט עם פריט אחד, שכותרתו "חזרה", המכנה אתshowMainMenuפונקציה. "תפריט" זה, שדומה יותר לכפתור, צריך להיות מיושר אנכית לתחתית ואופקית למעלה.
      class BackToMainMenuButton (Menu): def _init_ (self): super (BackToMainMenuButton, self)._ init_ ("") self.menu_valign = BOTTOM self.menu_halign = LEFT menuItems = [(MenuItem ("Back", showMainMenu))] self. create_menu (menuItems) 
    • הגדר את הפונקציה showCredits. זה צריך לעשות סצנה מתוך אMainMenuBgr שכבה ו נקודות זכות שכב והפעל את הסצנה.
      def showCredits (): credSc = Scene (MainMenuBgr ()) credSc.add (Credits ()) credSc.add (BackToMainMenuButton ()) director.run (credSc) 
  17. 17
    בדוק את הקוד שלך. כשאתה חושב שסיימת את הקוד שלך, עליך להסתכל שוב על כל זה. זה יכול לעזור לך להבחין אם ניתן לבצע אופטימיזציה למשהו, או שיש כמה שורות מיותרות ששכחת למחוק. אם פעלת לדוגמא, כל הקוד שלך אמור להיראות כך:
    • מ- cocos.director ייבוא * מ cocos.menu ייבוא * מ cocos.scene ייבוא * מ cocos.layer ייבוא * מ cocos.sprite יבוא * מ cocos.tiles יבוא * מ cocos.mapcolliders יבוא * מ cocos. ייבוא.text ייבוא תווית pyglet.app מ- pyglet.window מפתח ייבוא מתקרת ייבוא מתמטית מיבוא אקראי randint # "מצהיר" משתנים גלובליים מקלדת = 0 scrMang = 0 מחלקה MainMenuBgr (ColorLayer): def _init_ (עצמי): super (MainMenuBgr, עצמי)._ init_ (0,200,255,255) class MainMenu (תפריט): def _init_ (self): super (MainMenu, self)._ init_ ("") self.menu_valign = CENTER self.menu_halign = CENTER menuItems = [(MenuItem ("התחל משחק ", startGame)), (MenuItem (" Credits ", showCredits)), (MenuItem (" Quit ", pyglet.app.exit))] self.create_menu (menuItems) Credits (Layer):def _init_ (עצמי): סופר (קרדיטים, עצמי)._ init_ () credFile = open ("CREDITS", "r") creds = credFile.read () creds = creds.split ("\ n") עבור i בטווח (0, len (creds)): credLabel = תווית (creds [i], font_size = 32, anchor_x = "left", anchor_y = "top") credLabel.position = 25,500- (i + 1) * 40 self.add (credLabel) class BackToMainMenuButton (Menu): def _init_ (self): super (BackToMainMenuButton, self)._ init_ ("") self.menu_valign = BOTTOM self.menu_halign = LEFT menuItems = [(MenuItem ("Back", showMainMenu)] self.create_menu (menuItems) class FinishMenu (Menu): def _init_ (self, text): super (FinishMenu, self)._ init_ (text) self.menu_valign = CENTER self.menu_halign = CENTER menuItems = [(MenuItem ("נסה שוב ", startGame)), (MenuItem (" צא ", pyglet.app.exit)) עצמי.create_menu (menuItems) class PlatformerController (Action): מקלדת גלובלית, scrMang on_ground = True MOVE_SPEED = 300 JUMP_SPEED = 500 GRAVITY = -1200 def start (עצמי): self.target.velocity = (0, 0) שלב def (עצמי, dt): מקלדת גלובלית, גלילה אם dt> 0,1: # אל תעשו שום דבר בזמן השבתה גדולה מדי להחזיר vx, vy = self.target.velocity vx = (מקלדת [key.RIGHT] - מקלדת [key.LEFT]) * self.MOVE_SPEED vy + = self.GRAVITY * dt אם self.on_ground ומקלדת [key.SPACE]: vy = self.JUMP_SPEED dx = vx * dt dy = vy * dt last = self.target.get_rect () new = last.copy () new.x + = dx new.y + = dy self.target.velocity = self.target.collision_handler (last, new, vx, vy) self.on_ground = (new.y == last.y) self.target.position = new.center scrMang.set_focus (* new.center) posX, posY = self.target.position if posY <0: finishGame ("Game Over") להחזיר אם posX> 800 * 3: # level level finishGame ("רמה הושלמה") return def finishGame (text): menuSc = Scene (MainMenuBgr ()) menuSc.add (FinishMenu (text)) director.run (menuSc) def showCredits (): credSc = Scene (MainMenuBgr ()) credSc.add (Credits ()) credSc.add (BackToMainMenuButton ()) director.run (credSc) def createTilemap (): colAmount = ceil (800/16) * 3 # (רוחב המסך / גודל האריח) * 3 שורהAmount = ceil (600/16) # גובה המסך / גודל האריח tileFile = פתוח ("levelMap.xml", "w") tileFile.write ('<resource> \ n <דורש קובץ = "icyTiles.xml" /> \ n <rectmap id = "solid" origin = "00,0" tile_size = "16x16"> \ n ') iceHeight = randint (1,10) עבור i בטווח (0, colAmount): tileFile.write ('<column>') makeHole = False if randint (0,50) == 10 and i! = 0: # don 't לאפשר חורים בנקודה spawn makeHole = נכון עבור j בטווח (0, rowAmount): אם makeHole: tileFile.write ('<תא /> \ n') אחר: אם j <= iceHeight: tileFile.write ('<תא tile = "ice" /> \ n ') else: tileFile.write (' <cell /> \ n ') iceHeight = randint (iceHeight-5, iceHeight + 5) if iceHeight <0: # מגביל את האריח מלהגיע נמוך מדי iceHeight = randint (1,5) if iceHeight> rowAmount: # מגביל אריחים מלהגיע גבוה מדי iceHeight = randint (int (rowAmount / 2) -5, int (rowAmount / 2) +5) tileFile.write ('</ column > \ n ') tileFile.write (' </rectmap> \ n <rectmap id = "not_solid" origin = "00,0" tile_size = "16x16"> \ n ') עבור i בטווח (0, colAmount): tileFile.write ('<column>') עבור j בטווח (0, rowAmount): tileFile.write ('<cell tile = "sky" />\ n ') tileFile.write (' </column> \ n ') tileFile.write (' </rectmap> \ n </resource> \ n ') tileFile.close () def startGame (): מקלדת גלובלית, scrMang createTilemap () # fig = Sprite ('pingu.png') fig.position = (8, 250) figLayer = ScrollableLayer () figLayer.add (fig) # tileLayer = load ('levelMap.xml') solidTiles = tileLayer [' מוצק '] nsoliTiles = tileLayer [' not_solid '] # keyboard = key.KeyStateHandler () director.window.push_handlers (keyboard) # fig.do (PlatformerController ()) mapcollider = RectMapCollider (velocity_on_bump =' slide ') fig.collision_handler = make_collision_handler (mapcollider, solidTiles) # scrMang = ScrollingManager () scrMang.add (nsoliTiles, z = -1) scrMang.add (solidTiles, z = 0) scrMang.add (figLayer, z = 1) # gameSc = Scene (scrMang) director.run (gameSc) def showMainMenu ():menuSc = Scene (MainMenuBgr ()) menuSc.add (MainMenu ()) director.run (menuSc) חלון = director.init (כיתוב = "IcyPlat - פלטפורמה פשוטה", שינוי גודל = True) showMainMenu () 
    • אלה 168 שורות לגמרי, ו- 152 שורות אם סופרים רק את הקוד. זה נראה כמו הרבה, אבל עבור משחק כה מורכב, זה למעשה כמות קטנה.
  18. 18
    גמור. עכשיו בדקו את המשחק. כשאתם מתכנתים משהו, עליכם לבדוק האם זה עובד בכל פעם שיישמתם משהו חדש. כמו כן, ייתכן שתרצה לשחק את המשחק שכתבת במשך זמן מה.
מדריך זה יראה לכם כיצד לכתוב סוגים שונים של משחקי מחשב
מדריך זה יראה לכם כיצד לכתוב סוגים שונים של משחקי מחשב, עם דוגמאות ספציפיות כדי להתחיל.

חלק 3 מתוך 4: הכנת משחק עם גרפיקה תלת ממדית

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

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

  2. 2
    התקן את הכלים. ניתן להתקין את Panda3D עם. ניתן להתקין את בלנדר ממנהל החבילות של המערכת שלך או מאתר האינטרנט שלו. python3 -m pip install - extra-index-url https://archive.panda3d.org/ panda3d
  3. 3
    הכינו ספריה חדשה. עליך לשמור את כל הקבצים למשחק שלך בתיקיה זו. זה יאפשר לך לשמור על מחשב מסודר ולראות במהירות אילו קבצים יש לך לתוכנית.
  4. 4
    צור חלון ריק.
    • ייבוא ספרייה זה הכרחי כדי ליצור את החלון: from direct.showbase.ShowBase import ShowBase. כמו כן, ייבא הכל panda3d.core מהספרייה (עם from panda3d.core import *).
    • הגדר תת-מחלקה MyApp של ShowBase.
    • בפונקציית האתחול שלה, כתוב
      loadPrcFileData ('', 'פונג תלת מימד של חלון') 
      זו הפונקציה שמשנה את מאפייני החלונות, במקרה זה כיתוב החלון ל"פונג תלת ממדי ". לאחר מכן, אתחל את כיתת ההוריםShowBase.
    • צור אובייקט אפליקציה של הכיתה MyApp. הפעל אותו כדי להציג את החלון.
    • מ direct.showbase.ShowBase ייבוא ShowBase מ panda3d.core ייבוא * מחלקה MyApp (ShowBase): def _init_ (עצמי): loadPrcFileData ('', 'כותרת חלון 3D פונג') ShowBase._init_ (עצמי) app = MyApp () app.run () 
  5. 5
    צור מודל תלת ממדי. עליך ליצור תחילה את הדברים שאתה רוצה להציג במשחק תלת ממדי בתוכנית עריכה תלת ממדית, למשל בבלנדר. עליכם להתחיל במודל תלת מימד אחד, להוסיף אותו, ורק אז להמשיך לדגמים האחרים. בדרך זו, תימנעו מהצורך לחזור על עבודה רבה אם תעשה משהו לא בסדר בהתחלה. ודא כי דגמי התלת מימד שלך אינם מורכבים שלא לצורך, מכיוון שזה יכול להאט את המשחק.
    • פתח בלנדר. מחק את קוביית ברירת המחדל והוסף במקום זאת "Ico Sphere". זה לא נראה ממש כדורית בבלנדר עצמו, אבל נראה מספיק קרוב לתחום במשחק בפועל. היית יכול להשתמש בכדור ה- UV, אבל זה הרבה יותר מורכב ולכן לוקח יותר זמן לעיבוד.

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

  6. 6
    ייצא לפורמט שבו יכולה להשתמש ספריית ה- 3D שלך. כמו לתמונות דו מימד, ישנם פורמטים שונים עבור דגמי תלת מימד. אתה צריך להשתמש בספרייה שתלת מימד שלך יכולה להבין ולהראות. עיין בתיעוד שלו אם אינך בטוח באילו פורמטים הוא תומך.
    • לדוגמא, עליך לייצא את מודל הכדור לפורמט Panda3D. ראשית, שמור את המודל שלך כרגיל.תערובתקובץ. זה יאפשר לך לבצע שינויים אם אתה זקוק לכדור כדי להיראות אחרת. השתמש בשם קובץ סביר כלשהו שאתה זוכר, כמו ball.blend.
    • אפשר ייצוא לפורמט DirectX בבלנדר. לשם כך, עבור אל קובץהעדפות משתמש... או לחץ על Ctrl+ Alt+ U. בחלון שנפתח בחר בקטגוריה ייבוא וייצוא. למצואפורמט DirectX Xוסמן את תיבת הסימון מימין לה. לחץ על שמור הגדרות משתמש וסגור את החלון.
    • ייצא את המודל לפורמט DirectX X על ידי מעבר לקובץייצואDirectX (.x), וציין שם קובץ (שוב, בחר משהו כמו ball.x ולחץ על ייצא DirectX.
    • המרת DirectX .איקס ל- Panda3D .ביצה. Panda3D כבר מספק כלי לעשות זאת. זה נקראx2egg והתחביר הוא הבא: קלט x2egg.x output.egg. אז כדי להמיר קובץ, סוג שלך: x2egg ball.x ball.egg.
  7. 7
    טען את המודל לתוכנית שלך. זה מה שבעצם יאפשר לך לראות את זה בתוכנית ולעשות איתו משהו.
    • הגדר את צבע הרקע לשחור. זה יאפשר לך לראות טוב יותר את הדגמים שאתה טוען. זה נעשה כמו הגדרת הכיתוב, אך עם אפשרות אחרת:
      loadPrcFileData ('', 'צבע רקע 0 0 0 0') 
      הקפד לעשות זאת לפני אתחול החלון.
    • עבור לסוף ה- _init_פונקציה. טען את הדגם עם
      עצמי. כדור = מעמיס. loadModel ("ball.egg") 
      שים לב שקובץ הדגם צריך להיות באותה ספריה כמו קובץ התוכנית. טעינת הדגם עדיין לא תאפשר לו להופיע, אך עדיין יש צורך בכך. וגם העצמי. מול שם המשתנה, מה שהופך אותו לתכונה של הכיתה MyApp, יהיה שימושי בהמשך, אז עשו זאת מול כל אובייקט שתרצו לשנות מאוחר יותר.
    • הפוך את הדגם הטעון. זה נעשה עם ball.reparentTo(self.render).
    • הגדר את המיקום הנכון לכדור. זה צריך להיות ב 0, 0, 0 בהתחלה. הקואורדינטה הראשונה היא שמאלה / ימינה, השנייה היא קדימה / אחורה, השלישית היא למטה / למעלה. הפקודה לכך היא self.ball.setPos(0, 0, 0).
    • אם אתה עדיין לא רואה כלום, זה נורמלי. נסה להזיז את העכבר כלפי מעלה תוך כדי לחיצה על הלחצן הימני. אז אתה צריך לראות את זה. הסיבה לכך היא שהמצלמה נמצאת גם ב 0, 0, 0 - בתוך הכדור - כך שלא רואים אותה. זכות לחצן העכבר נע המצלמה קדימה ואחורה.
  8. 8
    הגדר את מיקום המצלמה. המצלמה צריכה להיות במצב שבו ניתן לראות הכל. מכיוון שזה לא בהכרח המקרה כברירת מחדל, ומכיוון שברירות המחדל יכולות להשתנות מפלטפורמה לפלטפורמה באותה תוכנה, עליך להגדיר את מיקום המצלמה במפורש.
    • ראשית, עליך להשבית את פקדי העכבר, אחרת Panda3D מסרב לכוון את המצלמה למיקום אחר בתוכנית. לאחר מכן, אתה יכול למעשה להגדיר את מיקום המצלמה. הקוד שלך אמור להיראות כך:
    • מ- direct.showbase.ShowBase ייבוא ShowBase מ panda3d.core ייבוא * מחלקה MyApp (ShowBase): def _init_ (עצמי): # אתחל את החלון loadPrcFileData ('', 'כותרת חלון 3D פונג') loadPrcFileData ('', 'רקע -צבע 0 0 0 0') ShowBase._init_ (עצמי) # Load ball model self.ball = loader.loadModel ("ball.egg") self.ball.reparentTo (self.render) self.ball.setPos (0, 0, 0) # הגדר מיקום מצלמה נכון self.disableMouse () camera.setPos (0, -30,0) app = MyApp () app.run () 
  9. 9
    הגדר את שאר הסצנה. בעת ביצוע וטעינה של מודל אחד עובד, תוכל להמשיך ליצור ולהוסיף את האחרים שאתה זקוק לסצנה שלך.
    • הוסף את הקירות והעטלפים. בצע את השלבים המתוארים עבור הכדור, אלא שאינך צריך להפעיל את יצואן DirectX שוב. למרות שיש ארבעה קירות ושני עטלפים, אתה צריך רק דגם אחד של שניהם. הפוך את הקיר למלבן דק המכסה את כל "רצפת הבלנדר" ואת העטלף לריבוע דק שגובהו כ -2 יחידות בלנדר. יהיה עליכם להגדיר את המיקומים, הסיבובים והקשקשים באופן ידני בקוד, כך שקצות הקירות נוגעים זה בזה בכדי ליצור צורה סגורה. אתה יכול לנסות למצוא את המספרים הנכונים בעצמך, או לחפש בקוד למטה, השייך ל-_init_פונקציה מתחת למקום בו טעון מודל הכדור. כמו כן, עליכם לכוון את המצלמה קרוב יותר למשתמש, ל- -60 במקום ל- -30.
    • # דגמי קירות עומס wallLeft = loader.loadModel ("wall.egg"); wallLeft.reparentTo (self.render) wallLeft.setPosHprScale (-15,0,0, 0,0,90, 2,2,1) wallRight = loader.loadModel ("wall.egg"); wallRight.reparentTo (self.render) wallRight.setPosHprScale (15,0,0, 0,0,90, 2,2,1) wallBottom = loader.loadModel ("wall.egg"); wallBottom.reparentTo (self.render) wallBottom.setPosHprScale (0,0,15, 0,0,0, 2,2,1) wallTop = loader.loadModel ("wall.egg"); wallTop.reparentTo (self.render) wallTop.setPosHprScale (0,0, -15, 0,0,0, 2,2,1) # טעינת דגמי עטלף self.batPlay = loader.loadModel ("bat.egg"); batPlay.reparentTo (self.render) self.batPlay.setPos (-5, -15, -5) self.batPlay.setScale (3,1,3) self.batOpp = loader.loadModel ("bat.egg"); batOpp.reparentTo (self.render) self.batOpp.setPos (5,15, -5) self.batOpp.setScale (3,1,3) 
  10. 10
    הוסף תאורה. עכשיו כל האובייקטים נמצאים, אבל זה לא ממש נראה טבעי וקשה לראות עומק. ניתן לתקן זאת באמצעות תאורה. שים לב שהאורות עצמם לא ייראו לעין וכי ישנם סוגים שונים של אורות. אלה שאתה צריך למשחק לדוגמא הם:
    • אורות נקודה. הם פולטים אורות לכל הכיוונים, כמו נורה קטנה לאין שיעור. מכיוון שהם מדליקים אובייקטים שונים שונים בגלל כיוון ומרחק, הם ייצרו צללים שיגרמו לסצנה להראות טבעית יותר.
    • אורות סביבה. אין להם ממש כיוון או עמדה, הם פשוט מדליקים את כל הסצנה באותה צורה. זה לא יכול לעזור לתפיסת עומק, אבל זה מוודא שניתן לראות הכל טוב.
    • הוסף את האורות עם הקוד הבא:
      # Lighting alight = AmbientLight ('alight') alight.setColor (VBase4 (0,1, 0,1, 0,1, 1)) alnp = render.attachNewNode (alight) render.setLight (alnp) plight = PointLight (' plight ') plight.setColor (VBase4 (0,9, 0,9, 0,9, 1)) plnp = render.attachNewNode (plight) plnp.setPos (0, -16,0) render.setLight (plnp) 
  11. 11
    הוסף פקדים. כמו בסוגים אחרים של משחקים, השחקן אמור להיות מסוגל לקיים אינטראקציה עם עולם המשחק בדרך כלשהי. כמו במשחקי דו מימד, דרך נפוצה לעשות זאת במשחקי תלת ממד היא לגרום לדמות לעשות משהו כאשר לוחצים על המקשים הנכונים.
    • עבור תוכנית זו, עליך להזיז את המחבט כשלוחצים על מקש. כאשר לוחצים על מקש, האירוע נקרא זהה למפתח. כאשר מקש מוחזק במקש, התוצאה היא סדרת אירועים הנקראת כמו המפתח עם-חזור בסופו של דבר.
    • הפוך את שיחת התוכנית לפונקציה כשלוחצים על מקש. זה נעשה עםעצמי. קבלפונקציה. כך, למשל, קריאה לפונקציהזוז שמאלה כאשר המפתח אהוא לחוץ ייעשה עם self.accept("a", moveLeft). כתוב את הקוד הבא שלך_init_ פונקציה:
      # הזז כאשר מקש לחץ על עצמי.קבל ("a", self.moveLeft) self.accept ("a-repeat", self.moveLeft) self.accept ("d", self.moveRight) self.accept ("d- חזור על ", self.moveRight) self.accept (" w ", self.moveUp) self.accept (" w-repeat ", self.moveUp) self.accept (" s ", self.moveDown) self.accept (" s-repeat ", self.moveDown) 
    • הגדר את הפונקציות הנקראות על ידי האירועים. הם יזיזו את העטלף של השחקן כראוי. וודא שהפונקציות עדיין בכיתהMyApp.
      def moveLeft (עצמי): self.batPlay.setX (self.batPlay.getX () - 1) def moveRight (עצמי): self.batPlay.setX (self.batPlay.getX () + 1) def moveUp (עצמי): self.batPlay.setZ (self.batPlay.getZ () + 1) def moveDown (self): self.batPlay.setZ (self.batPlay.getZ () - 1) 
  12. 12
    הוסף זיהוי התנגשות. איתור התנגשות מאפשר לך להבחין אם שני עצמים נמצאים זה בזה ולנקוט באמצעים הנכונים. אתה יכול להשתמש בו, למשל, כדי למנוע מהשחקן לעבור דרך קיר או לגרום למשהו שנזרק להקפיץ כשהוא פוגע ברצפה.
    • ראשית, עליכם לבצע את איתור ההתנגשות עבור העטלפים, מכיוון שתוכלו לבדוק זאת כעת. תוסיף את זיהוי ההתנגשות עבור הכדור מאוחר יותר, מכיוון שהוא דורש פעולות שונות.
    • הוסף חוצה התנגשות. זהו תנאי הכרחי לכל זיהוי התנגשות ב- Panda3D והוא נעשה עם
      בסיס. cTrav = CollisionTraverser () 
      בעת יישום זיהוי ההתנגשות, כדאי לראות האם התרחשה התנגשות. הפוך את ההתנגשויות לגלויות עם
      בסיס. cTrav. showCollisions (עיבוד) 
    • צור מודיע. כשמו כן הוא, אובייקט זה יודיע לתוכנית שחפצים מסוימים התנגשו או שעדיין מתנגשים. אתה יכול גם להודיע לו שחפצים מסוימים כבר לא מתנגשים, אך אינך זקוק לו למשחק זה.
      self.notifier = CollisionHandlerEvent () self.notifier.addInPattern ("% fn-in-% in") self.notifier.addAgainPattern ("% fn-again-% in") 
    • הפוך את התוכנית לקריאת פונקציה כששני אובייקטים מתנגשים. זה נעשה באותו אופן כמו בלחיצות המקשים. לדוגמא, אם המחבט של השחקן מתנגש בקיר השמאלי, האירוע נקרא"batPlay-in-wall Left". אז קוראים לפונקציהblockCollisionייעשה עם self.accept("batPlay-in-wallLeft", self.blockCollision).
    • הגדר תיבות התנגשות עבור כל האובייקטים שתרצה לזהות התנגשויות. לעת עתה זה אומר את כל הקירות ושני העטלפים. שימו לב שעליכם להוסיף את השורה base.cTrav.addCollider(batPlayColl, self.notifier) לכל אובייקט שיכול להתנגש במשהו (העטלפים במקרה זה), בעוד שכל אובייקט עם צורת התנגשות יכול להתנגש אוטומטית. תיבת התנגשות לוקח ארבעה טיעונים להיווצר: המיקום היחסי למרכז של האובייקט שהוא חל, ואת מידה x, y ו- z ביחס לכיוון לעצם הזה. לדוגמה:
      batPlayColl = self.batPlay.attachNewNode (CollisionNode ("batPlay")) batPlayColl.node (). addSolid (CollisionBox (LPoint3 (0,0,0), 1, 1, 1)) batPlayColl.show () 
    • הגדר את הפונקציה לאירועי ההתנגשות. מכיוון שההתנהגות בעצם זהה בכל המקרים, עליכם להגדיר רק פונקציה אחת שמטפלת בכל ההתנגשויות הללו בין עטלף לקיר. עליו להחזיר את העטלף למצב בו הוא לא מתנגש בקיר. באופן אידיאלי, זה יטופל על ידי קביעת המיקום שלentry.getFromNodePath (), אבל זה לא עובד, אז אתה צריך להתייחס לפעולות של שתי העטלפים כאל מקרים נפרדים.

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

      base.cTrav.showCollisions (render) וכל השורות הן שם של צורת התנגשות עם .הופעה() בסופו של דבר.
    • כל הקוד שלך אמור להיראות כך כך:
    • מתוך direct.showbase. ייבואמ- panda3d.core ייבוא * מחלקה MyApp (ShowBase): def _init_ (עצמי): # אתחל את חלון loadPrcFileData ('', 'כותרת חלון 3D פונג') loadPrcFileData ('', 'צבע רקע 0 0 0 0') ShowBase._init_ (עצמי) # אתחל בסיס זיהוי התנגשות base.cTrav = CollisionTraverser () base.cTrav.showCollisions (render) self.notifier = CollisionHandlerEvent () self.notifier.addInPattern ("% fn-in-% in") עצמי. notifier.addAgainPattern ("% fn-again-% in") self.accept ("batPlay-in-wallLeft", self.blockCollision) self.accept ("batPlay-again-wallLeft", self.blockCollision) self.accept ("batPlay-in-wallRight", self.blockCollision) self.accept ("batPlay-again-wallRight", self.blockCollision) self.accept ("batPlay-in-wallBottom", self.blockCollision) self.accept ("batPlay-again-wallBottom ", self.blockCollision) self.accept (" batPlay-in-wallTop ", self.blockCollision) self.accept (" batPlay-again-wallTop ", self.blockCollision) self.accept (" batOpp- in-wallLeft ", self.blockCollision) self.accept (" batOpp-again-wallLeft ", self.blockCollision) self.accept (" batOpp-in-wallRight ", self.blockCollision) self.accept (" batOpp-again- wallRight ", self.blockCollision) self.accept (" batOpp-in-wallBottom ", self.blockCollision) self.accept (" batOpp-again-wallBottom ", self.blockCollision) self.accept (" batOpp-in-wallTop ", self.blockCollision) self.accept ("batOpp-again-wallTop", self.blockCollision) # Load ball model self.ball = loader.loadModel ("ball.egg") self.ball.reparentTo (self.render) self.ball.setPos (0, 0,0) # דגמי קירות טען והגדר את תיבות ההתנגשות שלהם wallLeft = loader.loadModel ("wall.egg"); wallLeft.reparentTo (self.render) wallLeft.setPosHprScale (-15,0,0, 0,0,90, 2,2,1) wallLeftColl = wallLeft.attachNewNode (CollisionNode ("wallLeft")) wallLeftColl.node (). addSolid (CollisionBox (LPoint3 (0,0,0), 10, 10, 0,25)) wallLeftColl.show () wallRight = loader.loadModel ("wall.egg"); wallRight.reparentTo (self.render) wallRight.setPosHprScale (15,0,0, 0,0,90, 2,2,1) wallRightColl = wallRight.attachNewNode (CollisionNode ("wallRight")) wallRightColl.node (). addSolid (CollisionBox (LPoint3 (0,0,0), 10, 10, 0,25)) wallRightColl.show () wallBottom = loader.loadModel ("wall.egg"); wallBottom.reparentTo (self.render) wallBottom.setPosHprScale (0,0,15, 0,0,0, 2,2,1) wallBottomColl = wallBottom.attachNewNode (CollisionNode ("wallBottom")) wallBottomColl.node (). addSolid (CollisionBox (LPoint3 (0,0,0), 10, 10, 0,25)) wallBottomColl.show () wallTop = loader.loadModel ("wall.ביצה"); wallTop.reparentTo (self.render) wallTop.setPosHprScale (0,0, -15, 0,0,0, 2,2,1) wallTopColl = wallTop.attachNewNode (CollisionNode ("wallTop")) wallTopColl.node (). addSolid (CollisionBox (LPoint3 (0,0,0), 10, 10, 0,25)) wallTopColl.show () # טעינת דגמי עטלפים self.batPlay = loader.loadModel ("bat.egg"); self.batPlay.reparentTo (self.render) self.batPlay.setScale (3,1,3) self.batPlay.setPos (-5, -15, -5) batPlayColl = self.batPlay.attachNewNode (CollisionNode ("batPlay"))) batPlayColl.node (). addSolid (CollisionBox (LPoint3 (0,0,0), 1, 1, 1)) batPlayColl.show () base.cTrav.addCollider (batPlayColl, self.notifier) self.batOpp = מטעין.loadModel ("bat.egg"); self.batOpp.reparentTo (self.render) self.batOpp.setPos (5,15, -5) self.batOpp.setScale (3,1,3) batOppColl = self.batOpp.attachNewNode (CollisionNode ("batOpp")) batOppColl.node (). addSolid (CollisionBox (LPoint3 (0,0,0), 1, 1, 1)) batOppColl.show () base.cTrav.addCollider (batOppColl, self.notifier) # הגדר מיקום מצלמה נכון # עצמי.disableMouse () camera.setPos (0, -60,0) # Lighting alight = AmbientLight ('alight') alight.setColor (VBase4 (0,1, 0,1, 0,1, 1)) alnp = render. attachNewNode (alight) render.setLight (alnp) plight = PointLight ('plight') plight.setColor (VBase4 (0,9, 0,9, 0,9, 1)) plnp = render.attachNewNode (plight) plnp.setPos (0, -16,0) render.setLight (plnp) # העבר כאשר מקש נלחץ על self.accept ("a", self.moveLeft) self.accept ("a-repeat", self.moveLeft) self.accept (" d ",self.moveRight) self.accept ("d-repeat", self.moveRight) self.accept ("w", self.moveUp) self.accept ("w-repeat", self.moveUp) self.accept ("s ", self.moveDown) self.accept (" s-repeat ", self.moveDown) def moveLeft (self): self.batPlay.setX (self.batPlay.getX () - 1) def moveRight (self): self. batPlay.setX (self.batPlay.getX () + 1) def moveUp (self): self.batPlay.setZ (self.batPlay.getZ () + 1) def moveDown (self): self.batPlay.setZ (self. batPlay.getZ () - 1) def blockCollision (עצמי, כניסה): אם str (entry.getFromNodePath ()) == "render / bat.egg / batPlay": if str (entry.getIntoNodePath ()) == "render /wall.egg/wallLeft ": self.batPlay.setX (-15 + entry.getIntoNodePath (). getSx () + self.batPlay.getSx ()) if str (entry.getIntoNodePath ()) ==" render / wall.egg / wallRight ": self.batPlay.setX (15-entry.getIntoNodePath ().getSx () - self.batPlay.getSx ()) אם str (entry.getIntoNodePath ()) == "render / wall.egg / wallBottom": self.batPlay.setZ (15-entry.getIntoNodePath (). getSz () -self.batPlay.getSz ()) if str (entry.getIntoNodePath ()) == "render / wall.egg / wallTop": self.batPlay.setZ (-15 + entry.getIntoNodePath (). getSz () + עצמי.batPlay.getSz ()) if str (entry.getFromNodePath ()) == "render / bat.egg / batOpp": if str (entry.getIntoNodePath ()) == "render / wall.egg / wallLeft": עצמי.batOpp.setX (-15 + entry.getIntoNodePath (). getSx () + self.batOpp.getSx ()) אם str (entry.getIntoNodePath ()) == "render / wall.egg / wallRight": self.batOpp.setX (15-entry.getIntoNodePath (). getSx () - self.batPlay.getSx ()) אם str (entry.getIntoNodePath ()) == "render / wall.egg / wallBottom": self.batPlay.setZ (15-entry.getIntoNodePath (). GetSz () - self.batPlay.getSz ()) אם str (ערך.getIntoNodePath ()) == "render / wall.egg / wallTop": self.batPlay.setZ (-15 + entry.getIntoNodePath (). getSz () + self.batPlay.getSz ()) אם str (entry.getFromNodePath ()) == "render / bat.egg / batOpp": if str (entry.getIntoNodePath ()) == "render / wall.egg / wallLeft": self.batOpp.setX (-15 + entry.getIntoNodePath (). getSx () + self.batOpp.getSx ()) אם str (entry.getIntoNodePath ()) == "render / wall.egg / wallRight": self.batOpp.setX (15-entry.getIntoNodePath (). getSx () -self.batPlay.getSx ()) if str (entry.getIntoNodePath ()) == "render / wall.egg / wallBottom": self.batPlay.setZ (10-entry.getIntoNodePath (). getSz () - עצמי. batPlay.getSz ()) if str (entry.getIntoNodePath ()) == "render / wall.egg / wallTop": self.batPlay.setZ (-20 + entry.getIntoNodePath (). getSz () + self.batPlay. getSz ()) app = MyApp () app.run ()ביצה / wallTop ": self.batPlay.setZ (-15 + entry.getIntoNodePath (). getSz () + self.batPlay.getSz ()) אם str (entry.getFromNodePath ()) ==" render / bat.egg / batOpp ": if str (entry.getIntoNodePath ()) ==" render / wall.egg / wallLeft ": self.batOpp.setX (-15 + entry.getIntoNodePath (). getSx () + self.batOpp.getSx ()) אם str (entry.getIntoNodePath ()) == "render / wall.egg / wallRight": self.batOpp.setX (15-entry.getIntoNodePath (). getSx () - self.batPlay.getSx ()) אם str (entry.getIntoNodePath ()) == "render / wall.egg / wallBottom": self.batPlay.setZ (10-entry.getIntoNodePath (). getSz () - self.batPlay.getSz ()) אם str (ערך. getIntoNodePath ()) == "render / wall.egg / wallTop": self.batPlay.setZ (-20 + entry.getIntoNodePath (). getSz () + self.batPlay.getSz ()) app = יישום MyApp (). לרוץ()ביצה / wallTop ": self.batPlay.setZ (-15 + entry.getIntoNodePath (). getSz () + self.batPlay.getSz ()) אם str (entry.getFromNodePath ()) ==" render / bat.egg / batOpp ": if str (entry.getIntoNodePath ()) ==" render / wall.egg / wallLeft ": self.batOpp.setX (-15 + entry.getIntoNodePath (). getSx () + self.batOpp.getSx ()) אם str (entry.getIntoNodePath ()) == "render / wall.egg / wallRight": self.batOpp.setX (15-entry.getIntoNodePath (). getSx () - self.batPlay.getSx ()) אם str (entry.getIntoNodePath ()) == "render / wall.egg / wallBottom": self.batPlay.setZ (10-entry.getIntoNodePath (). getSz () - self.batPlay.getSz ()) אם str (ערך. getIntoNodePath ()) == "render / wall.egg / wallTop": self.batPlay.setZ (-20 + entry.getIntoNodePath (). getSz () + self.batPlay.getSz ()) app = יישום MyApp (). לרוץ()batPlay.getSz ()) if str (entry.getFromNodePath ()) == "render / bat.egg / batOpp": if str (entry.getIntoNodePath ()) == "render / wall.egg / wallLeft": עצמי. batOpp.setX (-15 + entry.getIntoNodePath (). getSx () + self.batOpp.getSx ()) אם str (entry.getIntoNodePath ()) == "render / wall.egg / wallRight": self.batOpp. setX (15-entry.getIntoNodePath (). getSx () - self.batPlay.getSx ()) if str (entry.getIntoNodePath ()) == "render / wall.egg / wallBottom": self.batPlay.setZ (10 -entry.getIntoNodePath (). getSz () - self.batPlay.getSz ()) if str (entry.getIntoNodePath ()) == "render / wall.egg / wallTop": self.batPlay.setZ (-20 + כניסה.getIntoNodePath (). getSz () + self.batPlay.getSz ()) app = MyApp () app.run ()batPlay.getSz ()) if str (entry.getFromNodePath ()) == "render / bat.egg / batOpp": if str (entry.getIntoNodePath ()) == "render / wall.egg / wallLeft": עצמי. batOpp.setX (-15 + entry.getIntoNodePath (). getSx () + self.batOpp.getSx ()) אם str (entry.getIntoNodePath ()) == "render / wall.egg / wallRight": self.batOpp. setX (15-entry.getIntoNodePath (). getSx () - self.batPlay.getSx ()) if str (entry.getIntoNodePath ()) == "render / wall.egg / wallBottom": self.batPlay.setZ (10 -entry.getIntoNodePath (). getSz () - self.batPlay.getSz ()) if str (entry.getIntoNodePath ()) == "render / wall.egg / wallTop": self.batPlay.setZ (-20 + כניסה.getIntoNodePath (). getSz () + self.batPlay.getSz ()) app = MyApp () app.run ()ביצה / wallLeft ": self.batOpp.setX (-15 + entry.getIntoNodePath (). getSx () + self.batOpp.getSx ()) אם str (entry.getIntoNodePath ()) ==" render / wall.egg / wallRight ": self.batOpp.setX (15-entry.getIntoNodePath (). getSx () - self.batPlay.getSx ()) if str (entry.getIntoNodePath ()) ==" render / wall.egg / wallBottom ": self.batPlay.setZ (10-entry.getIntoNodePath (). getSz () - self.batPlay.getSz ()) אם str (entry.getIntoNodePath ()) == "render / wall.egg / wallTop": self.batPlay.setZ (-20 + entry.getIntoNodePath (). getSz () + self.batPlay.getSz ()) app = MyApp () app.run ()ביצה / wallLeft ": self.batOpp.setX (-15 + entry.getIntoNodePath (). getSx () + self.batOpp.getSx ()) אם str (entry.getIntoNodePath ()) ==" render / wall.egg / wallRight ": self.batOpp.setX (15-entry.getIntoNodePath (). getSx () - self.batPlay.getSx ()) if str (entry.getIntoNodePath ()) ==" render / wall.egg / wallBottom ": self.batPlay.setZ (10-entry.getIntoNodePath (). getSz () - self.batPlay.getSz ()) אם str (entry.getIntoNodePath ()) == "render / wall.egg / wallTop": self.batPlay.setZ (-20 + entry.getIntoNodePath (). getSz () + self.batPlay.getSz ()) app = MyApp () app.run ()ביצה / wallBottom ": self.batPlay.setZ (10-entry.getIntoNodePath (). getSz () - self.batPlay.getSz ()) אם str (entry.getIntoNodePath ()) ==" render / wall.egg / wallTop ": self.batPlay.setZ (-20 + entry.getIntoNodePath (). getSz () + self.batPlay.getSz ()) app = MyApp () app.run ()ביצה / wallBottom ": self.batPlay.setZ (10-entry.getIntoNodePath (). getSz () - self.batPlay.getSz ()) אם str (entry.getIntoNodePath ()) ==" render / wall.egg / wallTop ": self.batPlay.setZ (-20 + entry.getIntoNodePath (). getSz () + self.batPlay.getSz ()) app = MyApp () app.run () 
  13. 13
    הוסף תנועה. לא רק שהשחקן צריך לראות תגובה כלשהי כאשר הוא לוחץ על מקש, יש גם חפצים שנעים בכוחות עצמם: בעזרת זה ניתן לדרוש מהנגן תגובה, או בדיוק כמו פרט רקע יפה.
    • לגרום לכדור לנוע. זה יעוף דרך הקירות לעת עתה, אבל תתקן זאת בשלב הבא.
    • ייבא את הפונקציות randint ו סדרי ראנד מ ה אקראיספריה. כמו כן, ייבאמשימה מ direct.task.
    • חשב את המהירות שהכדור אמור להיות בהתחלה. עבור לסוף ה-_init_פונקציה לשם כך. צור וקטור של 3 מספרים שלמים אקראיים. שים לב שמהירות ה- y היא תמיד זהה, רק שלילית או חיובית. נורמל את הווקטור הזה, כלומר שנה את מרכיביו כך שיישמר ביניהם, אך אורכו הכולל של הווקטור הוא 1. חלק את הווקטור המנורמל ב 5 כדי שהכדור לא יעוף מהר מדי.
      # הפוך את הכדור לזוז עצמי. BallSpeed = VBase3 (randint (-10,10), randrange (-1,1,2), randint (-10,10)) self.ballSpeed.normalize () self.ballSpeed / = 5 
    • צור משימה. ב- Panda3D, משימה פירושה לקרוא לפונקציה לכל פריים. כתוב את הקוד הבא תחת חישובי המהירות:
      עצמי. taskMgr. להוסיף (עצמי. updateBallPos, "UpdateBallPos") 
    • הגדר את פונקציית המשימה. הפונקציה צריכה פשוט להוסיף את המהירות למצב הכדור. ואז, הוא אמור לחזורTask.cont, מה שהופך את הפונקציה לקרוא שוב למסגרת הבאה.
      def updateBallPos (self, task): self.ball.setPos (self.ball.getPos () + self.ballSpeed) return Task.cont 
  14. 14
    הוסף זיהוי התנגשות גם עבור האובייקטים הנעים. שים לב לאובייקטים שנעים במהירות: הם עשויים לדרוש סוג מיוחד של זיהוי התנגשות הבודק גם במסגרות הקודמות כדי לקבוע אם האובייקטים התנגשו בכל עת, גם אם זה היה מהיר מכדי לקרות בכל מסגרת שהיא.
    • עליכם לגרום לכדור להקפיץ בכל פעם שהוא מתנגש במשהו. זה ימנע ממנו לעוף דרך הקירות או העטלפים.
    • אפשר זיהוי התנגשות נוזלים. עבור אובייקטים נעים במהירות כמו הכדור במשחק זה, יש בעיה בזיהוי התנגשות רגיל: אם האובייקט נמצא מול מה שהוא יתנגש במסגרת אחת וכבר מאחוריו במסגרת הבאה, ההתנגשות אינה ' לא זוהה. אבל זה לזהות התנגשויות כאלה עם כמה התאמות: עבור למקום בו ביצעת אתחול של חוצה ההתנגשות והוסף את השורה
      בסיס. cTrav. setRespectPrevTransform (נכון) 
      אז לך ל updateBallPosולהחליף setPos עם setFluidPos.
    • קבל אירועי התנגשות כדור. התוכנית שלך צריכה להבחין בהתנגשויות בין הכדור לקירות או למחבט. אל תוסיף אתשוב אירועים הפעם מכיוון שכדאי לוודא שהכדור משנה כיוון רק פעם אחת - אם הוא משנה כיוון פעמיים, הוא פשוט ממשיך לעוף דרך הקיר או המחבט.
    • תגדיר את להקפיץפונקציה שנקראת בכל פעם שהכדור מתנגש. כדי להפוך כיוון, הגדר אותו לשלילי. השתמש בכל כיוון שהכדור עמד לברוח אליו: למשל, אם הוא מתנגש בקיר שמאל או ימין, הפוך את כיוון ה- x.
      def bounceOff (עצמי, ערך): אם str (entry.getIntoNodePath ()) == "render / wall.egg / wallLeft" או str (entry.getIntoNodePath ()) == "render / wall.egg / wallRight": עצמי.ballSpeed [0] = -עצמי.ballSpeed [0] אם str (entry.getIntoNodePath ()) == "render / bat.egg / batPlay" או str (entry.getIntoNodePath ()) == "render / bat.egg / batOpp ": self.ballSpeed [1] = -self.ballSpeed [1] if str (entry.getIntoNodePath ()) ==" render / wall.egg / wallBottom "או str (entry.getIntoNodePath ()) ==" render / wall.egg / wallTop ": self.ballSpeed [2] = -self.ballSpeed [2] 
    • התאם ערכים בלתי הולמים. עכשיו אתה יכול לבדוק איך זה לשחק את המשחק, אם כי היריב יחמיץ את הכדור בסבירות גבוהה מאוד. אבל אתה יכול לבדוק אם אתה יכול לראות את הכדור היטב ולהכות אותו בעצמך. אתה יכול להחזיר את המצלמה ל -75 ואת העטלפים ל ± 25 למשחק קל יותר. אתה יכול גם להגדיל את הכדור כך שיהיה קל יותר לראות לאיזה כיוון הוא נע וכמה הוא קרוב. אתה יכול להאריך את הקירות מעט יותר (קנה מידה 3 במקום 2 בכיוון Y) כך שהכדור לא יכול לעוף מחוץ לשדה הראייה לפני שהוא נכנס מאחורי העטלף.
  15. 15
    הגדירו את התנהגות היריב. אם למשחק שלך יש יריב כלשהו, תצטרך לתכנת את התנהגותם מכיוון שסביר מאוד שלא יהיה לשחקן יריב אמיתי. אין ספק, ישנם משחקי רשת מרובי משתתפים בהם השחקן צריך לשחק מול נגן אחר במחשב אחר, אך קשה מאוד לתכנת אותם ולרבים מהם יש גם אפשרות לשחקן יחיד.
    • הוסף משימה נוספת. הפוך את השיחה הזו לפונקציה בשםdirectOpponent.
    • הגדר את הפונקציה directOpponent. פשוט לכוון את המחבט לעקוב אחר הכדור לכיוון X / Z זה קל. הבעיה היא שגם היריב צריך לעשות טעויות, כך שלשחקן יהיה סיכוי לנצח. ניתן לעשות זאת במידה הנכונה של אקראיות.
      • בפונקציה שלמטה, מחבט היריב נע בכיוון הנכון או בכיוון ההפוך. זה מאפשר לפעמים להחמיץ את הכדור.
      • הגדל את המספר החיובי אם אתה רוצה שהיריב יכה את הכדור בתדירות גבוהה יותר, והמספר השלילי יהיה נמוך יותר אם אתה רוצה שהוא יחמיץ את הכדור בתדירות גבוהה יותר. אם תעשה את שניהם, ההשפעות יבטלו זה את זה.
      def directOpponent (עצמי, משימה): dirX = randint (-2,4) * (self.ball.getX () - self.batOpp.getX ()) dirZ = randint (-2,4) * (כדור עצמי. getZ () - self.batOpp.getZ ()) self.batOpp.setX (self.batOpp.getX () + העתקה (1/7, dirX)) self.batOpp.setZ (self.batOpp.getZ () + העתקה (1/7, dirZ)) להחזיר Task.cont 
    • לשחק את המשחק. בעוד הכדור עדיין נעלם לנצח כאשר השחקן או היריב מפספסים, זה כבר אפשרי לבדוק את המשחק ולהתאים משהו במידת הצורך.
    • הפוך את תיבות ההתנגשות לבלתי נראות כעת. יש הרבה התנגשויות במשחק הזה, וההבהבות התמידית של הקופסאות יכולה להסיח את הדעת ולעצבן. אז הסר את השורהbase.cTrav.showCollisions (render) וכל השורות הן שם של צורת התנגשות עם .הופעה() בסוף, כמו למשל wallLeftColl.show ().
  16. 16
    קבע גבולות. אם למעט עצמים אחרים להתנגש בהם, לאובייקטים אין גבולות לאן הם יכולים לנוע, זה יכול לגרום לבעיות. אם, למשל, השחקן זורק כדור והוא לעולם לא יחזור, הם יהיו די מוטרדים מכך.
    • בדוגמה, התוכנית צריכה לזהות מתי הכדור מחוץ למגרש. אם זה קורה, התוכנית צריכה להחזיר אותה ל (00,0) ולתת נקודה לשחקן שלא החמיץ.
    • ייבא OnscreenText מ direct.gui.OnscreenText.
    • הגדר את הציון כרשימה. צריך שיהיו בו שני פריטים ששניהם מוגדרים ל -0 בהתחלה.
    • הצג את הטקסט כ- טקסט על המסך. המיקום שונה כאן: המספר הראשון הוא שמאלה / ימינה, והשני למטה / למעלה. לשניהם חצי מסך כיחידה אחת.fg קובע את צבע הטקסט.
      # ספירת הציונים self.scores = [0,0] self.scoreCount = OnscreenText (text = (str (self.scores [0]) + "" + str (self.scores [1])), pos = (0, 0,75), סולם = 0,1, fg = (0, 255, 0, 0,5)) 
    • הוסף שתי הצהרות אם ל- updateBallPosפונקציה. עליהם לבדוק אם הכדור הוא מעבר ל- 26 או ל- 26, ואם זה המקרה, להחזיר את הכדור ל (00,0) ולהגדיל את הציון המתאים (של השחקן או של היריב).
      def updateBallPos (עצמי, משימה): self.ball.setFluidPos (self.ball.getPos () + self.ballSpeed) אם self.ball.getY ()> 26: self.scores [0] + = 1 כדור עצמי. setPos (0,0,0) self.scoreCount.destroy () # הרס טקסט אחרון לפני הוספת self.scoreCount חדש = טקסט על המסך (text = (str (self.scores [0]) + "" + str (self.scores [1])), pos = (0, 0,75), סולם = 0,1, fg = (0, 255, 0, 0,5)) אם self.ball.getY () <-26: self.scores [1] + = 1 self.ball.setPos (0,0,0) self.scoreCount.destroy () self.scoreCount = OncreenText (text = (str (self.scores [0]) + "" + str (עצמי.scores [1])), pos = (0, 0,75), scale = 0,1, fg = (0, 255, 0, 0,5)) return Task.cont 
  17. 17
    בדוק את הקוד שלך. אם כתבת את המשחק בדוגמה, כל הקוד שלך צריך להיראות כך כך:
    • מ direct.showbase.ShowBase ייבוא ShowBase מ direct.task ייבוא משימה מ panda3d.core ייבוא * מ direct.gui.OnscreenText ייבוא OnscreenText מיבוא אקראי randint, סידור rand ממחשב העתק יישום העתק מתמטיקה MyApp (ShowBase): def _init_ (עצמי): # אתחל את חלון loadPrcFileData ('', 'כותרת חלון 3D פונג') loadPrcFileData ('', 'צבע רקע 0 0 0 0') ShowBase._init_ (עצמי) # אתחל זיהוי התנגשותbase.cTrav = CollisionTraverser () base.cTrav.setRespectPrevTransform (True) self.notifier = CollisionHandlerEvent () self.notifier.addInPattern ("% fn-in-% in") self.notifier.addAgainPattern ("% fn-again-% ב- ") self.accept (" batPlay-in-wallLeft ", self.blockCollision) self.accept (" batPlay-again-wallLeft ", self.blockCollision) self.accept (" batPlay-in-wallRight ", עצמי. blockCollision) self.accept ("batPlay-again-wallRight", self.blockCollision) self.accept ("batPlay-in-wallBottom", self.blockCollision) self.accept ("batPlay-again-wallBottom", self.blockCollision) self.accept ("batPlay-in-wallTop", self.blockCollision) self.accept ("batPlay-again-wallTop", self.blockCollision) self.accept ("batOpp-in-wallLeft", self.blockCollision) עצמי. לקבל("batOpp-again-wallLeft ", self.blockCollision) self.accept (" batOpp-in-wallRight ", self.blockCollision) self.accept (" batOpp-again-wallRight ", self.blockCollision) self.accept (" batOpp- in-wallBottom ", self.blockCollision) self.accept (" batOpp-again-wallBottom ", self.blockCollision) self.accept (" batOpp-in-wallTop ", self.blockCollision) self.accept (" batOpp-again- wallTop ", self.blockCollision) self.accept (" ball-in-wallLeft ", self.bounceOff) self.accept (" ball-in-wallRight ", self.bounceOff) self.accept (" ball-in-wallBottom ", self.bounceOff) self.accept ("ball-in-wallTop", self.bounceOff) self.accept ("ball-in-batPlay", self.bounceOff) self.accept ("ball-in-batOpp", עצמי.bounceOff) # Load ball model self.ball = loader.loadModel ("ball.ביצה ") self.ball.reparentTo (self.render) self.ball.setPos (0, 0, 0) ballColl = self.ball.attachNewNode (CollisionNode (" ball ")) ballColl.node (). addSolid (CollisionSphere (0, 0, 0, 0,25)) ballColl.show () base.cTrav.addCollider (ballColl, self.notifier) # טעינת דגמי קירות והגדרת תיבות ההתנגשות שלהם wallLeft = loader.loadModel ("wall.egg"); wallLeft.reparentTo (self.render) wallLeft.setPosHprScale (-15,0,0, 0,0,90, 2,3,1) wallLeftColl = wallLeft.attachNewNode (CollisionNode ("wallLeft")) wallLeftColl.node (). addSolid (CollisionBox (LPoint3 (0,0,0), 10, 10, 0,25)) wallRight = loader.loadModel ("wall.egg"); wallRight.reparentTo (self.render) wallRight.setPosHprScale (15,0, 0, 0,0,90, 2,3,1) wallRightColl = wallRight.attachNewNode (CollisionNode ("wallRight")) wallRightColl.node (). AddSolid (CollisionBox (LPoint3 (0,0,0), 10, 10, 0,25)) wallBottom = loader.loadModel ("wall.egg"); wallBottom.reparentTo (self.render) wallBottom.setPosHprScale (0,0,15, 0,0,0, 2,3,1) wallBottomColl = wallBottom.attachNewNode (CollisionNode ("wallBottom")) wallBottomColl.node (). addSolid (CollisionBox (LPoint3 (0,0,0), 10, 10, 0,25)) wallTop = loader.loadModel ("wall.egg"); wallTop.reparentTo (self.render) wallTop.setPosHprScale (0,0, -15, 0,0,0, 2,3,1) wallTopColl = wallTop.attachNewNode (CollisionNode ("wallTop")) wallTopColl.node (). addSolid (CollisionBox (LPoint3 (0,0,0), 10, 10, 0,25)) # טעינת דגמי עטלפים self.batPlay = loader.loadModel ("bat.egg"); self.batPlay.reparentTo (self.render) self.batPlay.setScale (3,1,3) self.batPlay.setPos (-5, -25, -5) batPlayColl = self.batPlay.attachNewNode (CollisionNode ("batPlay"))) batPlayColl.node ().addSolid (CollisionBox (LPoint3 (0,0,0), 1, 1, 1)) base.cTrav.addCollider (batPlayColl, self.notifier) self.batOpp = loader.loadModel ("bat.egg"); self.batOpp.reparentTo (self.render) self.batOpp.setPos (5,25, -5) self.batOpp.setScale (3,1,3) batOppColl = self.batOpp.attachNewNode (CollisionNode ("batOpp")) batOppColl.node (). addSolid (CollisionBox (LPoint3 (0,0,0), 1, 1, 1)) base.cTrav.addCollider (batOppColl, self.notifier) # הגדר מיקום מצלמה נכון self.disableMouse () מצלמה. setPos (0, -75,0) # Lighting alight = AmbientLight ('alight') alight.setColor (VBase4 (0,1, 0,1, 0,1, 1)) alnp = render.attachNewNode (alight) render. setLight (alnp) plight = PointLight ('plight') plight.setColor (VBase4 (0,9, 0,9, 0,9, 1)) plnp = render.attachNewNode (plight) plnp.setPos (0, -16, 0) render.setLight (plnp) # העבר כאשר מקש נלחץ על self.accept ("a ", self.moveLeft) self.accept (" a-repeat ", self.moveLeft) self.accept (" d ", self.moveRight) self.accept (" d-repeat ", self.moveRight) self.accept ("w", self.moveUp) self.accept ("w-repeat", self.moveUp) self.accept ("s", self.moveDown) self.accept ("s-repeat", self.moveDown) # הפוך את הכדור לנוע עצמי.בולספיד = VBase3 (randint (-10,10), randrange (-1,2,2) * 8, randint (-10,10)) self.ballSpeed.normalize () self.ballSpeed / = 7 self.taskMgr.add (self.updateBallPos, "UpdateBallPos") self.taskMgr.add (self.directOpponent, "DirectOpponent") # ספירת הציונים self.scores = [0,0] self.scoreCount = OnscreenText (text = (str (self.scores [0]) + "" + str (self.scores [1])), pos = (0, 0,75), scale = 0,1, fg = (0, 255, 0, 0,5)) def moveLeft (עצמי): self.batPlay.setX (עצמי.batPlay.getX () - 1) def moveRight (עצמי): self.batPlay.setX (self.batPlay.getX () + 1) def moveUp (עצמי): self.batPlay.setZ (self.batPlay.getZ () + 1) def moveDown (עצמי): self.batPlay.setZ (self.batPlay.getZ () - 1) def blockCollision (עצמי, כניסה): אם str (entry.getFromNodePath ()) == "render / bat.egg / batPlay ": אם str (entry.getIntoNodePath ()) ==" render / wall.egg / wallLeft ": self.batPlay.setX (-15 + entry.getIntoNodePath (). getSx () + self.batPlay.getSx ()) אם str (entry.getIntoNodePath ()) == "render / wall.egg / wallRight": self.batPlay.setX (15-entry.getIntoNodePath (). getSx () - self.batPlay.getSx ()) אם str (entry.getIntoNodePath ()) == "render / wall.egg / wallBottom": self.batPlay.setZ (15-entry.getIntoNodePath (). getSz () - self.batPlay.getSz ()) אם str (ערך. getIntoNodePath ()) == "render / wall.egg / wallTop": self.batPlay.setZ (-15 + ערך.getIntoNodePath (). getSz () + self.batPlay.getSz ()) if str (entry.getFromNodePath ()) == "render / bat.egg / batOpp": if str (entry.getIntoNodePath ()) == "render /wall.egg/wallLeft ": self.batOpp.setX (-15 + entry.getIntoNodePath (). getSx () + self.batOpp.getSx ()) if str (entry.getIntoNodePath ()) ==" render / wall.egg / wallRight ": self.batOpp.setX (15-entry.getIntoNodePath (). getSx () - self.batOpp.getSx ()) אם str (entry.getIntoNodePath ()) ==" render / wall.egg / wallBottom ": self.batOpp.setZ (15-entry.getIntoNodePath (). getSz () - self.batOpp.getSz ()) if str (entry.getIntoNodePath ()) ==" render / wall.egg / wallTop ": self.batOpp.setZ (-15 + entry.getIntoNodePath (). getSz () + self.batOpp.getSz ()) def bounceOff (self, entry): if str (entry.getIntoNodePath ()) == "render / wall.egg / wallLeft "או str (entry.getIntoNodePath ()) ==" render / wall.ביצה / wallRight ": self.ballSpeed [0] = -עצמך.ballSpeed [0] אם str (entry.getIntoNodePath ()) ==" render / bat.egg / batPlay "או str (entry.getIntoNodePath ()) == "render / bat.egg / batOpp": self.ballSpeed [1] = -self.ballSpeed [1] if str (entry.getIntoNodePath ()) == "render / wall.egg / wallBottom" או str (entry.getIntoNodePath ()) == "render / wall.egg / wallTop": self.ballSpeed [2] = -self.ballSpeed [2] def updateBallPos (self, task): self.ball.setFluidPos (self.ball.getPos () + self.ballSpeed) אם self.ball.getY ()> 26: self.scores [0] + = 1 self.ball.setPos (0,0,0) self.scoreCount.destroy () # להרוס את הטקסט האחרון לפני הוספת self.scoreCount חדש = OncreenText (טקסט = (str (self.scores [0]) + "" + str (self.scores [1])), pos = (0, 0,75), סולם = 0,1, fg = (0, 255, 0, 0,5)) אם self.ball.getY () <-26: self.scores [1] + = 1 עצמי.ball.setPos (0,0,0) self.scoreCount.destroy () self.scoreCount = OncreenText (text = (str (self.scores [0]) + "" + str (self.scores [1])), pos = (0, 0,75), סולם = 0,1, fg = (0, 255, 0, 0,5)) החזר Task.cont def directOpponent (עצמי, משימה): dirX = randint (-2,4) * (self.ball.getX () - self.batOpp.getX ()) dirZ = randint (-2,4) * (self.ball.getZ () - self.batOpp.getZ ()) self.batOpp. setX (self.batOpp.getX () + copysign (1/7, dirX)) self.batOpp.setZ (self.batOpp.getZ () + copysign (1/7, dirZ)) להחזיר Task.cont app = MyApp () app.run ()ball.getZ () - self.batOpp.getZ ()) self.batOpp.setX (self.batOpp.getX () + העתקה (1/7, dirX)) self.batOpp.setZ (self.batOpp.getZ () + copysign (1/7, dirZ)) החזר Task.cont app = MyApp () app.run ()ball.getZ () - self.batOpp.getZ ()) self.batOpp.setX (self.batOpp.getX () + העתקה (1/7, dirX)) self.batOpp.setZ (self.batOpp.getZ () + copysign (1/7, dirZ)) החזר Task.cont app = MyApp () app.run () 
    • אלה 166 שורות לגמרי, ו- 152 שורות של קוד טהור. משחקי תלת מימד הם מורכבים מאוד, ולכן מדובר בכמות שורות רגילה למשחק כזה.
  18. 18
    גמור. למשחק זה אין אפשרות לנצח או להפסיד בשלב מסוים, ואין אפשרות להפעיל אותו מחדש ללא הפעלה מחדש של התוכנית. תכונות כאלה יהפכו את התוכנית שלך למורכבת עוד יותר, אז אם אתה רוצה יותר תרגול, נסה ליישם אותם. בכל מקרה, כדאי לך לשחק במשחק זמן מה כדי לבדוק אם הכל עובד כמו שצריך, ואולי באמת להשתעשע עם המשחק שכתבת.
אז אתה צריך לוודא שכל משתמש שמתקין את התוכנית שלך גם מתקין את כל מה שהתוכנית צריכה כדי לעבוד
אז אתה צריך לוודא שכל משתמש שמתקין את התוכנית שלך גם מתקין את כל מה שהתוכנית צריכה כדי לעבוד.

חלק 4 מתוך 4: פרסום המשחק

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

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

  3. 3
    החליטו על התנאים שעליהם תרצו לפרסם את המשחק. אמנם עליכם להקפיד על המגבלות שקובעות המדיה החיצונית שלכם, אך בדרך כלל תוכלו להחליט על רישיון הקוד שלכם בעצמכם. אתה יכול להשתמש ברישיון מתיר מאוד, כמו CC0. אתה יכול להשתמש ברישיון המאפשר הפצה ושינוי בתנאים מסוימים, כמו רישיון BSD או GPL. לחלופין, אתה יכול להפוך את התוכנה שלך לקניינית, כלומר אף אחד לא רשאי להפיץ אותה או לשנות אותה ללא רשותך.
    • החלט אם ברצונך למכור את התוכנה שלך. למרות שניתן להרוויח כסף על ידי מכירת תוכנה, זה לא סביר שאנשים יקנו את התוכנה הראשונה שלך שיש לה בדרך כלל מעט תכונות ושום דבר מיוחד. כמו כן, אם תוכנית חינמית לא עובדת, אנשים שהורידו אותה פשוט יתאכזבו; אם הם שילמו על זה, עם זאת, הם ידרשו את כספם בחזרה, ויגרמו לבעיות נוספות הן עבורכם והן עבור המשתמשים. אז שקול להפוך את התוכניות הראשונות שלך לזמינות בחינם.
  4. 4
    החלט כיצד ברצונך לפרסם את המשחק שלך. לכל שיטה יש כמה יתרונות וחסרונות, אז אתה צריך להחליט בעצמך.
    • פרסום האתר: אם יש לך אתר, תוכל להעלות עליו את המשחק שלך. דאג לתת למבקרים הוראות כיצד להתקין לא רק את התוכנה, אלא גם את התלות. החיסרון בשיטה זו הוא שהמשתמש התקין את התלות באופן ידני, מה שעשוי להיות קשה מדי עבור אנשים שרק רוצים לשחק במשחק, ומשימה די משעממת.
    • הכנת חבילה למנהל חבילה: ישנם מנהלי חבילות שונים, כמו apt, yum או homebrew. לכולם יש פורמטים שונים של חבילות. עיין בהפניה עבור הפורמט המתאים. הדבר הטוב בחבילות מנהל החבילות הוא שהם יתקינו באופן אוטומטי את כל התלות אם תגדיר אותם נכון. אז המשתמש צריך להתקין רק את החבילה שלך ואז הוא יכול לשחק במשחק. הבעיה היא שיש הרבה מנהלי חבילות בפלטפורמות שונות, כך שתצטרכו להשקיע קצת עבודה במתן חבילות לכל הנפוצות ביותר.
  5. 5
    תשומת לב ישירה לתוכנית שלך. שקול להעלות את התוכנית שלך למאגר חבילות ראשי, כמו אלה שמתחזקים אובונטו ודביאן. זה יאפשר למשתמשים להתקין אותו בקלות רבה. כמו כן, פרסם בפורומים מתאימים, כמו קטע הפרויקטים של GameDev או חלק מ- tigSource. אבל אל תתאכזב אם המשחקים הראשונים שלך לא יתפרסמו. המשך הלאה, אם יש לך מושג שאנשים רבים אוהבים ומקודדים אותו, המשחק שלך יכול להיות ידוע.
לשחקן יש הרבה יותר דרכים לקיים אינטראקציה עם התוכנית במשחק דו-ממדי מאשר במשחק מבוסס טקסט
לשחקן יש הרבה יותר דרכים לקיים אינטראקציה עם התוכנית במשחק דו-ממדי מאשר במשחק מבוסס טקסט.

טיפים

  • וודא שאתה סבלני ומוכן ללמוד - תכנות יכול להיות מתסכל לפעמים!
  • אם אתה תוהה איך עושים משהו במשחק אחר, והמשחק הוא קוד פתוח, אתה יכול להסתכל על קוד המקור שלו.
  • כשאתה מחפש מדיה, חפש במפורש אחר בעל רישיון חופשי. אפשרות לעשות זאת כוללת ציון כל אחת מהאפשרויות של Creative Commons או Public Domain בחיפוש התמונות במנוע החיפוש שלך. דרך נוספת היא להשתמש באתר המספק מדיה מורשית באופן חופשי, כמו https://opengameart.org או https://publicdomainpictures.net.

אזהרות

  • אל תעתיק נתחי קוד גדולים מבלי לבדוק את רישיון הקוד. לרוב זה אסור, ואם לא, בדרך כלל דורש ייחוס.
  • אל תעשה דואר זבל ואל תפרסם במקומות לא הולמים כשאתה מקדם את המשחק שלך. זה עשוי לגרום לכם לחסימה מהדף המושפע, פשוט מעצבן, ויפגע במוניטין שלכם.
מאמרים בנושאים דומים
  1. איך משחקים 20 שאלות?
  2. כיצד להימנע מהונאת הגרלות?
  3. איך מחזירים למשחק בקיטור?
  4. איך מכינים שוקולד חם חד קרן?
  5. איך לשחק בונקו?
  6. איך לשחק רובוט אדום?
FacebookTwitterInstagramPinterestLinkedInGoogle+YoutubeRedditDribbbleBehanceGithubCodePenWhatsappEmail