JavaRush /مدونة جافا /Random-AR /قسم "الألعاب" في JavaRush: نظرية مفيدة

قسم "الألعاب" في JavaRush: نظرية مفيدة

نشرت في المجموعة
ستجد في قسم "الألعاب" في JavaRush مشاريع مثيرة لكتابة ألعاب الكمبيوتر الشهيرة. هل ترغب في إنشاء نسختك الخاصة من الألعاب الشهيرة "2048" و"Sapper" و"Snake" وغيرها من الألعاب؟ انه سهل. لقد حولنا كتابة اللعبة إلى عملية خطوة بخطوة. الفصللتجرب نفسك كمطور ألعاب، ليس من الضروري أن تكون مبرمجًا متقدمًا، ولكن لا تزال هناك حاجة إلى مجموعة معينة من المعرفة بـ Java. ستجد هنا المعلومات التي ستكون مفيدة عند كتابة الألعاب .

1. الميراث

يتضمن العمل مع محرك اللعبة JavaRush استخدام الميراث. ولكن ماذا لو كنت لا تعرف ما هو؟ من ناحية، عليك أن تفهم هذا الموضوع: يتم دراسته في المستوى 11 . من ناحية أخرى، تم تصميم المحرك عمدًا ليكون بسيطًا جدًا، حتى تتمكن من تدبر أمرك بمعرفة سطحية عن الميراث. إذن ما هو الميراث؟ بكل بساطة، الميراث هو العلاقة بين طبقتين. يصبح أحدهما الوالد، والثاني يصبح الطفل (الطبقة الوريثة). في هذه الحالة، قد لا تعرف الفئة الأصل أن لديها فئات فرعية. أولئك. ولا يتلقى أي فائدة خاصة من وجود الطبقات الوارثة. لكن الميراث يوفر العديد من المزايا للفئة المتحدرة. والأمر الرئيسي هو أن جميع متغيرات وأساليب الفئة الأصل تظهر في الفصل الفرعي، كما لو تم نسخ رمز الفصل الأصلي إلى الفصل الفرعي. هذا ليس صحيحا تماما، ولكن لفهم مبسط للميراث سيكون كافيا. فيما يلي بعض الأمثلة لفهم الميراث بشكل أفضل. مثال 1: أبسط الميراث.
public class Родитель {

}
ترث فئة الطفل من فئة الأصل باستخدام الكلمة الأساسية الممتدة .
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;
   }
}
يمكن للفئة التابعة استخدام متغيرات وأساليب الفئة الأصل كما لو تم الإعلان عنها فيها. في هذا المثال نستخدم طريقة 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 الخاصة بنا، نعلن ببساطة عن طريقة بنفس التوقيع (الرأس) مثل طريقة الفئة الأصل ونكتب الكود الخاص بنا فيها. مثال 1: تجاوز الطريقة.
public class Родитель {

   public String name;

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

   public getName() {
      return name;
  }
}
ستطبع طريقة printInfo () العبارة "Luke, No!!!"
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(من الفئة الأصل) في الفئة السليلة، فعندما يتم استدعاء هذه الطريقة على كائن من الفئة السليلة، سيتم استدعاء طريقتها getName()، وليس getName()الفئة الأصل.
Родитель 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. فئة قائمة المصفوفات
  2. العمل 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 بالضبط، كما هو الحال في نظام الإحداثيات الكلاسيكي. تعمل بقية أساليب المحرك بطريقة مماثلة. عند تطوير الألعاب، غالبًا ما تكون هناك حاجة لإعادة إنتاج حالة المصفوفة التي تظهر على الشاشة. كيف نفعل ذلك؟ أولاً، في الحلقة، تحتاج إلى تكرار جميع عناصر المصفوفة. ثانيًا، بالنسبة لكل واحد منهم، قم باستدعاء طريقة للعرض بإحداثيات معكوسة. مثال:
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. أي رمز الحصول على 999 كقيمة عشوائية قصوى. سيبدو الرقم العشوائي المكون من ثلاثة أرقام كما يلي:
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] - أطلق على الحدث "نوم زائد"، إذا كان الرقم ضمن [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