JavaRush /בלוג Java /Random-HE /סעיף "משחקים" ב-JavaRush: תיאוריה שימושית

סעיף "משחקים" ב-JavaRush: תיאוריה שימושית

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

1. ירושה

עבודה עם מנוע המשחק JavaRush כרוכה בשימוש בירושה. אבל מה אם אתה לא יודע מה זה? מצד אחד, אתה צריך להבין את הנושא הזה: הוא נלמד ברמה 11 . מצד שני, המנוע תוכנן בכוונה להיות פשוט מאוד, כך שאפשר להסתדר עם ידע שטחי של ירושה. אז מהי ירושה? בפשטות רבה, ירושה היא מערכת היחסים בין שני מחלקות. אחד מהם הופך להורה, והשני הופך לילד (כיתת יורש). במקרה זה, ייתכן שכיתת האב אפילו לא יודעת שיש לה כיתות צאצאים. הָהֵן. הוא אינו מקבל תועלת מסוימת מנוכחותם של מעמדות יורשים. אבל הירושה מספקת יתרונות רבים למעמד צאצא. והעיקרי הוא שכל המשתנים והשיטות של כיתת האב מופיעים בכיתה הילד, כאילו הקוד של כיתת האב הועתק לכיתה הילד. זה לא לגמרי נכון, אבל להבנה פשוטה של ​​ירושה זה יתאים. הנה כמה דוגמאות כדי להבין טוב יותר את הירושה. דוגמה 1: הירושה הפשוטה ביותר.
public class Родитель {

}
הכיתה Child יורשת ממחלקת האב באמצעות מילת המפתח extends .
public class Потомок extends Родитель {

}
דוגמה 2: שימוש במשתני מחלקות אב.
public class Родитель {

   public int age;
   public String name;
}
כיתת הילד יכולה להשתמש במשתני הגיל והשמות של כיתת האב כאילו הוכרזו בה.
public class Потомок extends Родитель {

   public void printInfo() {

     System.out.println(name+" "+age);
   }
}
דוגמה 3: שימוש בשיטות מחלקות אב.
public class Родитель {

   public int age;
   public String name;

   public getName() {
      return name;
   }
}
המחלקה Child יכולה להשתמש במשתנים ובשיטות של מחלקת Parent כאילו הם הוכרזו בה. בדוגמה זו אנו משתמשים בשיטת getName ().
public class Потомок extends Родитель {

   public void printInfo() {

     System.out.println(getName()+" "+age);
   }
}
כך נראית המחלקה Descendant מנקודת המבט של המהדר:
public class Потомок extends Родитель {

   public int age; //  унаследованная переменная
   public String name; //  унаследованная переменная

   public getName() { //  унаследованный метод.
      return name;
  }
   public void printInfo() {

     System.out.println(getName()+" "+age);
   }
}

2. עקיפת שיטה

לפעמים יש מצבים שירשנו את מחלקת ה-Descendant שלנו ממחלקת Parent מאוד שימושית, יחד עם כל המשתנים והשיטות, אבל יש שיטות שלא עובדות בדיוק כמו שאנחנו רוצים. או בכלל לא בצורה שאנחנו לא רוצים. מה לעשות במצב הזה? אנחנו יכולים לעקוף שיטה שאנחנו לא אוהבים. זה נעשה בצורה פשוטה מאוד: במחלקת Descendant שלנו אנחנו פשוט מכריזים על מתודה עם אותה חתימה (header) כמו שיטת Parent class וכותבים בה את הקוד שלנו. דוגמה 1: עקיפת שיטה.
public class Родитель {

   public String name;

   public void setName (String nameNew) {
       name = nameNew;
  }

   public getName() {
      return name;
  }
}
שיטת printInfo() תדפיס את הביטוי "לוק, לא!!!"
public class Потомок extends Родитель {

   public void setName (String nameNew) {
       name = nameNew + ",No!!!";
  }

   public void printInfo() {

      setName("Luke");
      System.out.println( getName());
   }
}
כך נראית המחלקה Descendant מנקודת המבט של המהדר:
public Потомок extends Родитель {

   public String name; //  унаследованная переменная

   public void setName (String nameNew) { //  Переопределенный метод взамен унаследованного

       name = nameNew + ", No!!!";
   }
   public getName() { //  унаследованный метод.

      return name;
   }
   public void printInfo() {

     setName("Luke");
     System.out.println(getName());
   }
}
דוגמה 2: קסם קטן של ירושה (והחלפת השיטה).
public class Родитель {

   public getName() {
      return "Luke";
  }
   public void printInfo() {

     System.out.println(getName());
   }
}
public class Потомок extends Родитель {

   public getName() {
      return "I'm your father, Luke";
  }
}
בדוגמה זו: אם מתודה printInfo(ממחלקת Parent) אינה מועברת במחלקה Descendant, כאשר מתודה זו נקראת על אובייקט של המחלקה Descendant, השיטה שלה תיקרא getName(), ולא getName()מחלקה Parent.
Родитель parent = new Родитель ();
parent.printnInfo();
קוד זה מציג את הכיתוב "לוק" על המסך .
Потомок child = new Потомок ();
child.printnInfo();
קוד זה מציג את הכתובת "אני אביך, לוק; .
כך נראית המחלקה Descendant מנקודת המבט של המהדר:
public class Потомок extends Родитель {

   public getName() {
      return "I'm your father, Luke";
   }
   public void printInfo() {

     System.out.println(getName());
   }
}

3. רשימות

אם עדיין לא פגשתם את רשימות, הנה הסבר קצר. אתה יכול למצוא מידע מלא על רמות 6-7 של קורס JavaRush . לרשימות יש הרבה מן המשותף עם מערכים:
  • יכול לאחסן הרבה נתונים מסוג מסוים;
  • לאפשר לך לאחזר אלמנטים לפי האינדקס/מספר שלהם;
  • מדדי אלמנט מתחילים ב-0.
יתרונות של רשימות: בניגוד למערכים, רשימות יכולות לשנות את גודלן באופן דינמי. מיד לאחר יצירת הרשימה יש גודל של 0. ככל שמוסיפים אלמנטים לרשימה, הגודל שלה גדל. דוגמה ליצירת רשימה:
ArrayList<String> myList = new ArrayList<String>(); // создание нового списка типа ArrayList
הערך בסוגריים של הזווית הוא סוג הנתונים שהרשימה יכולה לאחסן. הנה כמה שיטות לעבודה עם רשימה:
קוד תיאור קצר של מה הקוד עושה
ArrayList<String> list = new ArrayList<String>(); יצירת רשימה חדשה של מחרוזות
list.add("name"); הוסף אלמנט לסוף הרשימה
list.add(0, "name"); הוסף אלמנט לתחילת הרשימה
String name = list.get(5); קבל אלמנט לפי האינדקס שלו
list.set(5, "new name"); שנה אלמנט לפי האינדקס שלו
int count = list.size(); קבל את מספר האלמנטים ברשימה
list.remove(4); הסר פריט מרשימה
תוכל ללמוד עוד על רשימות מהמאמרים הבאים:
  1. מחלקה ArrayList
  2. Working ArrayList בתמונות
  3. הסרת אלמנט מ-ArrayList

4. מערכים

מהי מטריצה? מטריצה ​​היא לא יותר מטבלה מלבנית שניתן למלא בנתונים. במילים אחרות, זהו מערך דו מימדי. כפי שאתה בוודאי יודע, מערכים ב-Java הם אובייקטים. סוג מערך חד מימדי סטנדרטי intנראה כך:
int [] array = {12, 32, 43, 54, 15, 36, 67, 28};
בואו נדמיין את זה בצורה ויזואלית:
0 1 2 3 4 5 6 7
12 32 43 54 15 36 67 28
השורה העליונה מציינת את כתובות התא. כלומר, כדי לקבל את המספר 67, אתה צריך לגשת לאלמנט המערך עם אינדקס 6:
int number = array[6];
הכל מאוד פשוט כאן. מערך דו מימדי הוא מערך של מערכים חד מימדיים. אם זו הפעם הראשונה שאתה שומע על זה, עצור ותדמיין את זה בראש שלך. מערך דו מימדי נראה בערך כך:
0 מערך חד מימדי מערך חד מימדי
1 מערך חד מימדי
2 מערך חד מימדי
3 מערך חד מימדי
4 מערך חד מימדי
5 מערך חד מימדי
6 מערך חד מימדי
7 מערך חד מימדי
בקוד:
int [][] matrix = {
{65, 99, 87, 90, 156, 75, 98, 78}, {76, 15, 76, 91, 66, 90, 15, 77}, {65, 96, 17, 25, 36, 75, 54, 78}, {59, 45, 68, 14, 57, 1, 9, 63}, {81, 74, 47, 52, 42, 785, 56, 96}, {66, 74, 58, 16, 98, 140, 55, 77}, {120, 99, 13, 90, 78, 98, 14, 78}, {20, 18, 74, 91, 96, 104, 105, 77} }
0 0 1 2 3 4 5 6 7
65 99 87 90 156 75 98 78
1 0 1 2 3 4 5 6 7
76 15 76 91 66 90 15 77
2 0 1 2 3 4 5 6 7
65 96 17 25 36 75 54 78
3 0 1 2 3 4 5 6 7
59 45 68 14 57 1 9 63
4 0 1 2 3 4 5 6 7
81 74 47 52 42 785 56 96
5 0 1 2 3 4 5 6 7
66 74 58 16 98 140 55 77
6 0 1 2 3 4 5 6 7
120 99 13 90 78 98 14 78
7 0 1 2 3 4 5 6 7
20 18 74 91 96 104 105 77
כדי לקבל את הערך 47, עליך לגשת לאלמנט המטריצה ​​ב-[4][2].
int number = matrix[4][2];
אם תשים לב, קואורדינטות המטריצה ​​שונות ממערכת הקואורדינטות המלבנית הקלאסית (מערכת הקואורדינטות הקרטזית). בעת גישה למטריצה, אתה מציין קודם את y ולאחר מכן את x , בעוד שבמתמטיקה מקובל לציין קודם את x(x, y). אולי אתם שואלים את עצמכם, "למה לא להפוך את המטריצה ​​בדמיונכם ולגשת לאלמנטים בדרך הרגילה דרך (x, y)? זה לא ישנה את תוכן המטריצה". כן, שום דבר לא ישתנה. אבל בעולם התכנות, נהוג להתייחס למטריצות בצורה "קודם y, ואז x". יש לקחת זאת כמובן מאליו. עכשיו בואו נדבר על הקרנת המטריצה ​​על המנוע שלנו (מחלקה Game). כידוע, למנוע יש שיטות רבות שמשנות את התאים של שדה המשחק בקואורדינטות נתונות. לדוגמה, ה setCellValue(int x, int y, String value). זה מגדיר תא מסוים עם קואורדינטות (x, y) לערך value. כפי ששמתם לב, שיטה זו לוקחת תחילה בדיוק x, כמו במערכת הקואורדינטות הקלאסית. שאר שיטות המנוע פועלות בצורה דומה. בעת פיתוח משחקים, לרוב יהיה צורך לשחזר את מצב המטריצה ​​על המסך. איך לעשות את זה? ראשית, בלולאה אתה צריך לחזור על כל האלמנטים של המטריצה. שנית, עבור כל אחד מהם, קרא שיטה להצגה עם קואורדינטות INVERTED. דוגמא:
private void drawScene() {
    for (int i = 0; i < matrix.length; i++) {
        for (int j = 0; j < matrix[i].length; j++) {
            setCellValue(j, i, String.valueOf(matrix[i][j]));
        }
    }
}
באופן טבעי, היפוך פועל בשני כיוונים. setCellValueאפשר להעביר (i, j) למתודה , אבל במקביל לקחת את האלמנט [j][i] מהמטריצה. ההיפוך אולי נראה קצת קשה, אבל זה משהו שצריך לזכור. ותמיד, אם מתעוררות בעיות, כדאי לקחת פיסת נייר בעט, לצייר מטריצה ​​ולשחזר את התהליכים שקורים לה.

5. מספרים אקראיים

איך עובדים עם מחולל מספרים אקראיים? המחלקה Gameמגדירה שיטה getRandomNumber(int). מתחת למכסה המנוע, הוא משתמש בכיתה Randomמחבילת java.util, אבל זה לא משנה את עקרון העבודה עם מחולל מספרים אקראיים. getRandomNumber(int)לוקח מספר שלם כטיעון . מספר זה יהיה הגבול העליון שהמחולל יכול להחזיר. הגבול התחתון הוא 0. חָשׁוּב! המחולל לעולם לא יחזיר מספר סף עליון. לדוגמה, אם נקרא getRandomNumber(3)באופן אקראי הוא יכול להחזיר 0, 1, 2. כפי שאתה יכול לראות, הוא לא יכול להחזיר 3. שימוש זה בגנרטור הוא די פשוט, אך יעיל מאוד במקרים רבים. אתה צריך לקבל מספר אקראי בתוך כמה מגבלות: תאר לעצמך שאתה צריך איזה מספר תלת ספרתי (100..999). כפי שאתה כבר יודע, המספר המינימלי המוחזר הוא 0. אז, תצטרך להוסיף לו 100. אבל במקרה זה, אתה צריך לדאוג לא לחרוג מהגבול העליון. כדי לקבל 999 כערך האקראי המקסימלי, עליך לקרוא למתודה getRandomNumber(int)עם ארגומנט של 1000. אבל אנחנו זוכרים לגבי התוספת של 100 שלאחר מכן: זה אומר שצריך להוריד את הגבול העליון ב-100. כלומר, הקוד לקבל מספר אקראי בן שלוש ספרות ייראה כך:
int number = 100 + getRandomNumber(900);
אבל כדי לפשט הליך כזה, המנוע מספק שיטה getRandomNumber(int, int)שלוקחת את המספר המינימלי כדי להחזיר כארגומנט הראשון. באמצעות שיטה זו, ניתן לשכתב את הדוגמה הקודמת:
int number = getRandomNumber(100, 1000);
ניתן להשתמש במספרים אקראיים כדי לקבל אלמנט של מערך אקראי:
String [] names = {"Andrey", "Валентин", "Сергей"};
String randomName = names[getRandomNumber(names.length)]
הפעלת אירועים מסוימים בהסתברות מסוימת. הבוקר של אדם מתחיל לפי תרחישים אפשריים: שינה יתר - 50%; קם בזמן - 40%; קם שעה מוקדם מהצפוי - 10%. תאר לעצמך שאתה כותב אמולטור בוקר אנושי. אתה צריך להפעיל אירועים בהסתברות מסוימת. כדי לעשות זאת, שוב, עליך להשתמש במחולל מספרים אקראיים. ההטמעות עשויות להיות שונות, אך היישום הפשוט ביותר צריך לפעול לפי האלגוריתם הבא:
  1. אנו מגדירים את הגבולות שבהם צריך להפיק את המספר;
  2. ליצור מספר אקראי;
  3. אנו מעבדים את המספר המתקבל.
אז, במקרה זה, המגבלה תהיה 10. בואו נקרא לשיטה getRandomNumber(10)וננתח מה היא יכולה להחזיר לנו. הוא יכול להחזיר 10 ספרות (מ-0 עד 9) וכל אחת עם אותה הסתברות - 10%. כעת עלינו לשלב את כל התוצאות האפשריות ולהתאים אותן לאירועים האפשריים שלנו. יכולים להיות הרבה שילובים, תלוי בדמיון שלך, אבל הצלילים הכי ברורים: "אם מספר אקראי נמצא בתוך [0..4] - קרא לאירוע "Overslept", אם המספר נמצא בטווח של [5..8] ] - "להתעורר" בזמן," ורק אם המספר הוא 9, אז "קמתי שעה מוקדם מהצפוי." הכל מאוד פשוט: בתוך [0..4] יש 5 מספרים שכל אחד מהם יכול לחזור בהסתברות של 10%, שבסך הכל יהיה 50%; בתוך [5..8] יש 4 מספרים, ו-9 הוא המספר היחיד שמופיע בהסתברות של 10%. בקוד, כל העיצוב החכם הזה נראה אפילו יותר פשוט:
int randomNumber = getRandomNumber(10);
if (randomNumber < 5) {
    System.out.println("Проспал ");
} else if (randomNumber < 9) {
    System.out.println("Встал вовремя ");
} else {
    System.out.println("Встал на час раньше положенного ");
}
באופן כללי, יכולות להיות הרבה אפשרויות לשימוש במספרים אקראיים. הכל תלוי רק בדמיון שלך. אבל הם משמשים בצורה היעילה ביותר אם אתה צריך לקבל תוצאה כלשהי שוב ושוב. אז תוצאה זו תהיה שונה מהקודמת. עם סבירות מסוימת, כמובן. זה הכל! אם אתה רוצה ללמוד עוד על קטע המשחקים, הנה כמה תיעוד שימושי שיכול לעזור:
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION