1. Наследование

Работа с игровым движком JavaRush подразумевает использование наследования. Но что делать, если вы не знаете, что это такое? С одной стороны, нужно в этой теме разобраться и изучить ее. С другой стороны, т.к. движок специально спроектирован очень простым, там можно обойтись поверхностным знанием наследования.

Итак, что же такое наследование? Если говорить упрощенно, наследование — это связь между двумя классами. Один из них становится родителем, а второй — потомком (классом-наследником). При этом класс-родитель может даже не знать, что у него есть классы-потомки. Т.е. особой выгоды от наличия классов-наследников он не получает.

А вот классу-потомку наследование дает много преимуществ. И главное из них в том, что все переменные и методы класса-родителя появляются в классе-потомке, как будто код класса-родителя скопировали в класс-потомок. Это не совсем так, но для базового понимания наследования пойдет.

Вот несколько примеров, чтобы лучше понять наследование.

Пример 1 — самый простой пример с наследованием

public class Родитель
{
}
Класс Потомок унаследован от класса Родитель с помощью ключевого слова extends.
public class Потомок extends Родитель
{
}

Пример 2 — использование переменных класса-родителя

public class Родитель
{
  public int age;
  public String name;
}
Класс Потомок может использовать переменные age и 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);
   }
}

Если опустить некоторые подробности, можно сказать, что с точки зрения Java-компилятора мы просто скопировали код класса-родителя внутрь кода класса-потомка:

public class Потомок extends Родитель
{
   public int age;        // унаследованная переменная
   public String name;    // унаследованная переменная
   public getName() {     // унаследованный метод
      return name;
   }

   public void printInfo()
   {
      System.out.println(getName() + " " + age);
   }
}
класс Потомок с точки зрения компилятора


2. Переопределение методов

Иногда бывают ситуации, когда мы унаследовали наш класс Потомок от какого-то очень полезного нам класса Родитель, унаследовали все его переменные и методы, но вот некоторые методы работают не совсем, как нам хочется, или так, как нам совсем не хочется.

Что делать в этой ситуации? Мы можем переопределить непонравившийся нам метод. Делается это очень просто: в нашем классе Потомок мы просто объявляем метод с такой же сигнатурой (заголовком), что и метод класса Родитель и пишем в нем наш код.

Пример 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());
   }
}

Грубо говоря, при наследовании код класса-родителя копируется внутрь класса-потомка. Но если в классе-потомке уже есть такой же метод, как в классе-родителе, такой метод из класса-родителя не копируется. Тогда говорят, что метод класса-потомка перекрывает (переопределяет) метод класса-родителя. Посмотрите на пример ниже, возможно, станет немного понятнее:

Вот как выглядит класс Потомок с точки зрения компилятора:
public class Потомок 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() класса Родитель.

Если же метод printInfo() вызвать у объекта класса Потомок, он в свою очередь вызовет метод getName() класса Потомок.

Т.е. метод printInfo() объявлен только в классе Родитель, но он будет вызывать метод getName() класса Потомок, если сам метод printInfo() вызвать у объекта типа Потомок.

Пример:

Родитель parent = new Родитель();
parent.printnInfo();
Этот код выведет на экран надпись:
Luke
Потомок child = new Потомок();
child.printnInfo();
Этот код выведет на экран надпись:
I'm your father, Luke

А все потому, что с точки зрения компилятора (очень упрощенная версия) код класса Потомок выглядит так:

public class Потомок extends Родитель
{
   public getName() {
      return "I'm your father, Luke";
   }

   public void printInfo()
   {
      System.out.println(getName());
   }
}
класс Потомок с точки зрения компилятора


3. Списки

Краткое напоминание информации о списках (List). У списков есть много общего с массивами:

  • Могут хранить много данных определенного типа.
  • Позволяют получать элементы по их индексу/номеру.
  • Индексы элементов начинаются с 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);
Удалить элемент из списка

Для получения более подробной информации о списках, можете ознакомиться со статьями:



4. Случайные числа

У игрового движка JavaRush есть два метода, которые можно использовать для получения случайных чисел. Это методы:

int getRandomNumber(int max)
int getRandomNumber(int min, int max)

Первый метод — getRandomNumber(int max) возвращает случайное целое число в диапазоне 0, 1, 2, ... max-1. Под капотом он использует класс Random из пакета java.util, но принцип работы с генератором случайных чисел от этого не меняется.

В качестве аргумента getRandomNumber(int) принимает целое число. Это число будет верхней границей, которую может вернуть генератор случайных чисел. Нижней границей является 0. Внимание! Генератор НИКОГДА не вернёт верхнее граничное число. Например, если вызвать getRandomNumber(3) он случайным образом может вернуть 0, 1, 2. Как видно, 3 он вернуть не может. Такое использование генератора является довольно простым, но очень эффективным во многих случаях.

Второй метод — getRandomNumber(int min, int max) возвращает случайное целое число в диапазоне [min, max-1]. Он никогда не вернет число, меньше min, и никогда не вернет число, больше max-1.

Как же эти методы использовать на практике?

1. Игральные кости

Допустим, вы хотите имитировать бросок игральных костей и получить случайное число в диапазоне 1-6, как это сделать? Это можно сделать с помощью кода вида:

int dice = getRandomNumber(1, 7);

Такой метод вернет случайное целое число в диапазоне 1-6.

2. Стрельба по мишени

Допустим вам нужно имитировать выстрел по мишени, и у вашего выстрела есть случайное отклонение в диапазоне от -10 до +10 включительно. Это можно сделать с помощью кода вида:

int dx = getRandomNumber(-10, 11);

Такой метод вернет случайно целое число в диапазоне -10 до +10.

Вариантов применения случайных чисел в играх может быть очень много. Все зависит только от вашей фантазии. Пишите свои игры, кастомизируйте их и получайте удовольствие от процесса.

Играть в игры могут все, создавать их — только программисты.