CodeGym /مدونة جافا /Random-AR /قسم الألعاب في CodeGym: نظرية مفيدة
John Squirrels
مستوى
San Francisco

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

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

1. الميراث

يتضمن العمل مع محرك ألعاب CodeGym استخدام الميراث. ولكن ماذا لو كنت لا تعرف ما هو؟ من ناحية، عليك أن تفهم هذا الموضوع: يتم دراسته في المستوى 11 . من ناحية أخرى، تم تصميم المحرك خصيصًا ليكون بسيطًا جدًا، حتى تتمكن من الإفلات من المعرفة السطحية بالميراث. إذن ما هو الميراث؟ بكل بساطة، الميراث هو علاقة بين طبقتين. يصبح أحدهما والدًا والآخر طفلًا (سليلًا). علاوة على ذلك، قد لا تعرف الطبقة الأم أن لها أحفادًا. وبعبارة أخرى، فإنه لا يحصل على أي ميزة خاصة من خلال وجود أحفاد. لكن الميراث يعطي السليل العديد من المزايا. والأهم من ذلك هو أن جميع متغيرات وأساليب الفئة الأصلية تظهر في الفئة التابعة كما لو تم نسخ رمز الفئة الأصلية إلى الفئة التابعة. هذا ليس وصفًا دقيقًا تمامًا، ولكنه يكفي لفهم مبسط للميراث. فيما يلي بعض الأمثلة لفهم الميراث بشكل أفضل. مثال 1: أبسط ميراث.
public class Parent {

}
ترث فئة الطفل فئة الأصل باستخدام الكلمة الأساسية الممتدة .
public class Child extends Parent {

}
مثال 2: استخدام متغيرات الفئة الأصل.
public class Parent {

   public int age;
   public String name;
}
يمكن لفئة الطفل استخدام متغيرات العمر والاسم للفئة الأصلية كما لو تم الإعلان عنها في فئة الأصل .
public class Child extends Parent {

   public void printInfo() {

     System.out.println(name+" "+age);
   }
}
مثال 3: استخدام أساليب الفصل الأصلي.
public class Parent {

   public int age;
   public String name;

   public getName() {
      return name;
  }
}
يمكن لفئة الطفل استخدام متغيرات وأساليب الفئة الأصل كما لو تم الإعلان عنها في فئة الطفل. في هذا المثال، نستخدم طريقة getName() .
public class Child extends Parent {

   public void printInfo() {

     System.out.println(getName()+" "+age);
   }
}
هذا ما تبدو عليه فئة الطفل للمترجم:
public class Child extends Parent{

   public int age;  // Inherited variable
   public String name;  // Inherited variable

   public getName() {  // Inherited method.
      return name;
  }
   public void printInfo() {

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

2. أساليب التجاوز

في بعض الأحيان تكون هناك مواقف نجعل فيها فئة الطفل ترث بعض فئات الوالدين المفيدة جدًا، بالإضافة إلى جميع المتغيرات والأساليب الخاصة بها، ولكن بعض الأساليب لا تعمل تمامًا كما نريدها. أو لا على الإطلاق كما نريدهم. ماذا يمكننا أن نفعل في هذه الحالة؟ يمكننا تجاوز الطريقة التي لا نحبها. من السهل جدًا القيام بذلك: في فئة الطفل الخاصة بنا، نعلن ببساطة عن طريقة بنفس التوقيع مثل الطريقة الموجودة في فئة الأصل، ثم نكتب الكود الخاص بنا فيها. مثال 1: تجاوز الطريقة.
public class Parent {

   public String name;

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

   public getName() {
      return name;
  }
}
ستعرض طريقة printInfo() ‎ "Luke, No!!!"
public class Child extends Parent{

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

   public void printInfo() {
      setName("Luke");
      System.out.println(getName());
   }
}
هذا ما تبدو عليه فئة الطفل للمترجم:
public Child extends Parent {

   public String name;  // Inherited variable

   public void setName(String nameNew)  // Overridden method instead of the inherited method {

       name = nameNew + ", No!!!";
   }
   public getName() {  // Inherited method.

      return name;
   }
   public void printInfo() {

     setName("Luke");
     System.out.println( getName());
   }
}
مثال 2: بعض سحر الميراث (وتجاوز الطريقة).
public class Parent {

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

     System.out.println(getName());
   }
}
public class Child extends Parent {

   public getName() {
      return "Luke, I am your father";
  }
}
في هذا المثال، إذا printInfoلم يتم تجاوز الطريقة (من فئة الأصل) في فئة الطفل، فعند استدعاء هذه الطريقة على كائن تابع، getName()سيتم استدعاء طريقتها بدلاً من طريقة فئة الأصل getName().
Parent parent = new Parent ();
parent.printnInfo();
يعرض هذا الرمز "لوقا" على الشاشة.
Child child = new Child ();
child.printnInfo();
يعرض هذا الرمز "لوقا، أنا والدك" على الشاشة.
هذا ما تبدو عليه فئة الطفل للمترجم:
public class Child extends Parent {

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

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

3. القوائم

إذا لم تقابل القوائم (القائمة) بعد، فإليك نظرة عامة مختصرة. يمكنك العثور على معلومات كاملة في المستويات 6-7 من دورة CodeGym . هناك الكثير من القواسم المشتركة بين القوائم والمصفوفات:
  • يمكنك تخزين الكثير من البيانات من نوع معين؛
  • أنها تسمح لك بالحصول على العناصر من خلال فهرسها؛
  • تبدأ مؤشرات العناصر من 0.
فوائد القوائم: على عكس المصفوفات، يمكن للقوائم تغيير حجمها ديناميكيًا. عند إنشاء قائمة، يكون حجمها 0. وكلما قمت بإضافة عناصر إلى القائمة، يزداد حجمها. فيما يلي مثال لإنشاء القائمة:
ArrayList<String> myList = new ArrayList<String>(); // Create a new 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. قائمة المصفوفات في الصور
  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) مساوية لمعلمة القيمة. ربما لاحظت أن هذه الطريقة تأخذ 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]));
        }
    }
}
وبطبيعة الحال، فإن الانعكاس يعمل في كلا الاتجاهين. يمكنك تمرير (i, j) إلى الطريقة setCellValueوأخذ العنصر [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 = {"Sarah", "Val", "Sergey"};
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، فلدينا حدث "استيقظت مبكرًا بساعة". الأمر كله بسيط للغاية. هناك 5 أرقام في النطاق [0 ..4]، يمكن إرجاع كل منها باحتمال 10%، ليصبح المجموع 50%؛ هناك 4 أرقام في النطاق [5..8]، حسنًا، و9 هو مجرد رقم واحد يظهر مع احتمال 10%. يبدو هذا التصميم الرائع أسهل في البرمجة:
int randomNumber = getRandomNumber(10);
if (randomNumber < 5) {
    System.out.println("Overslept");
} else if (randomNumber < 9) {
    System.out.println("Woke up on time");
} else {
    System.out.println("Woke up an hour early");
}
بشكل عام، هناك العديد من الطرق لاستخدام الأرقام العشوائية. أنت محدود فقط بخيالك. ولكن يتم استخدامها بشكل أكثر فعالية إذا كنت بحاجة إلى الحصول على بعض النتائج بشكل متكرر. ثم ستكون النتيجة الجديدة مختلفة عن النتيجة السابقة. مع بعض الاحتمالية بالطبع. هذا كل شئ حتى الان! إذا كنت تريد معرفة المزيد حول قسم "الألعاب"، فإليك بعض الوثائق المفيدة التي يمكن أن تساعدك:
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION