CodeGym /Blog Java /Random-PL /Sekcja gier na CodeGym: Przydatna teoria
John Squirrels
Poziom 41
San Francisco

Sekcja gier na CodeGym: Przydatna teoria

Opublikowano w grupie Random-PL
W sekcji „Gry” na CodeGym znajdziesz ekscytujące projekty związane z pisaniem popularnych gier komputerowych. Chcesz stworzyć własną wersję popularnych gier 2048, Minesweeper, Snake i innych gier? To proste. Zmieniliśmy pisanie gier w proces krok po kroku. Sekcja "Gry" na CodeGym: Przydatna teoria - 1Aby sprawdzić swoje umiejętności jako twórcy gier, nie trzeba być zaawansowanym programistą, ale potrzebny jest konkretny zasób wiedzy o Javie. Tutaj znajdziesz informacje, które będą przydatne w pisaniu gier .

1. Dziedziczenie

Praca z silnikiem gry CodeGym wiąże się z wykorzystaniem dziedziczenia. Ale co, jeśli nie wiesz, co to jest? Z jednej strony musisz zrozumieć ten temat: jest on studiowany na poziomie 11. Z drugiej strony silnik został specjalnie zaprojektowany tak, aby był bardzo prosty, dzięki czemu możesz uciec od powierzchownej wiedzy o dziedziczeniu. Czym zatem jest dziedziczenie? Mówiąc bardzo prosto, dziedziczenie to związek między dwiema klasami. Jeden z nich staje się rodzicem, a drugi dzieckiem (potomkiem). Co więcej, klasa nadrzędna może nawet nie wiedzieć, że ma potomków. Innymi słowy, posiadanie potomków nie przynosi żadnej szczególnej korzyści. Ale dziedziczenie daje potomkowi wiele korzyści. A najważniejsze jest to, że wszystkie zmienne i metody klasy nadrzędnej pojawiają się w potomku, tak jakby kod klasy nadrzędnej został skopiowany do klasy potomnej. Nie jest to całkowicie dokładny opis, ale wystarczy do uproszczonego zrozumienia dziedziczenia. Przykład 1: Najprostsze dziedziczenie.

public class Parent {

}
Klasa Child dziedziczy klasę Parent za pomocą słowa kluczowego extends .

public class Child extends Parent {

}
Przykład 2: Używanie zmiennych klasy nadrzędnej.

public class Parent {

   public int age;
   public String name;
}
Klasa Child może używać zmiennych wieku i imienia klasy Parent tak, jakby zostały zadeklarowane w klasie Parent .

public class Child extends Parent {

   public void printInfo() {

     System.out.println(name+" "+age);
   }
}
Przykład 3: Korzystanie z metod klasy nadrzędnej.

public class Parent {

   public int age;
   public String name;

   public getName() {
      return name;
  }
}
Klasa Child może używać zmiennych i metod klasy Parent tak, jakby były zadeklarowane w klasie Child. W tym przykładzie używamy metody getName() .

public class Child extends Parent {

   public void printInfo() {

     System.out.println(getName()+" "+age);
   }
}
Tak wygląda klasa Child dla kompilatora:

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. Nadrzędne metody

Czasami zdarzają się sytuacje, w których nasza klasa Child dziedziczy bardzo przydatną klasę Parent wraz ze wszystkimi jej zmiennymi i metodami, ale niektóre metody nie działają tak, jak byśmy tego chcieli. Albo wcale nie tak, jakbyśmy tego chcieli. Co możemy zrobić w tej sytuacji? Możemy zastąpić metodę, która nam się nie podoba. Jest to bardzo proste: w naszej klasie Child po prostu deklarujemy metodę z taką samą sygnaturą jak metoda w klasie Parent, a następnie piszemy w niej własny kod. Przykład 1: Przesłonięcie metody.

public class Parent {

   public String name;

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

   public getName() {
      return name;
  }
}
Metoda printInfo() wyświetli komunikat „Luke, No!!!”

public class Child extends Parent{

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

   public void printInfo() {
      setName("Luke");
      System.out.println(getName());
   }
}
Tak wygląda klasa Child dla kompilatora:

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());
   }
}
Przykład 2: Trochę magii dziedziczenia (i nadpisywania metod).

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";
  }
}
W tym przykładzie, jeśli printInfometoda (z klasy Parent) nie zostanie przesłonięta w klasie Child, wywołanie tej metody na obiekcie Child getName()spowoduje wywołanie jej metody zamiast getName()metody klasy Parent.

Parent parent = new Parent ();
parent.printnInfo();
Ten kod wyświetla „Luke” na ekranie.

Child child = new Child ();
child.printnInfo();
Ten kod wyświetla na ekranie komunikat „Luke, jestem twoim ojcem” .
Tak wygląda klasa Child dla kompilatora:

public class Child extends Parent {

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

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

3. Listy

Jeśli jeszcze nie spotkałeś się z listami (Lista), oto krótki przegląd. Pełne informacje znajdziesz na poziomach 6-7 kursu CodeGym . Listy mają wiele wspólnego z tablicami:
  • możesz przechowywać wiele danych określonego typu;
  • pozwalają ci uzyskać przedmioty według ich indeksu;
  • indeksy elementów zaczynają się od 0.
Korzyści z list: W przeciwieństwie do tablic, listy mogą zmieniać rozmiar dynamicznie. Gdy lista jest tworzona, jej rozmiar wynosi 0. W miarę dodawania elementów do listy jej rozmiar zwiększa się. Oto przykład tworzenia listy:

ArrayList<String> myList = new ArrayList<String>(); // Create a new ArrayList
Wartość w nawiasach ostrych wskazuje typ danych, który może przechowywać lista. Oto kilka metod pracy z listą:
Kod Krótki opis działania kodu
ArrayList<String> list = new ArrayList<String>(); Utwórz nową listę ciągów
list.add("name"); Dodaj element na końcu listy
list.add(0, "name"); Dodaj element na początek listy
String name = list.get(5); Pobierz element według jego indeksu
list.set(5, "new name"); Zmień element według jego indeksu
int count = list.size(); Pobierz liczbę elementów na liście
list.remove(4); Usuń element z listy
Więcej informacji na temat list można znaleźć w następujących artykułach:
  1. Klasa ArrayList
  2. ArrayList na zdjęciach
  3. Usuwanie elementu z ArrayList

4. Tablice

Co to jest macierz? Macierz to nic innego jak prostokątna tabela, którą można wypełnić danymi. Innymi słowy, jest to tablica dwuwymiarowa. Jak zapewne wiesz, tablice w Javie są obiektami. Standardowa jednowymiarowa inttablica wygląda następująco:

int [] array = {12, 32, 43, 54, 15, 36, 67, 28};
Możemy to zwizualizować tak:
0 1 2 3 4 5 6 7
12 32 43 54 15 36 67 28
Górny wiersz wskazuje adresy komórek. Innymi słowy, aby uzyskać liczbę 67, musisz uzyskać dostęp do elementu tablicy o indeksie 6:

int number = array[6];
To wszystko jest bardzo proste. Tablica dwuwymiarowa to tablica tablic jednowymiarowych. Jeśli słyszysz o tym po raz pierwszy, zatrzymaj się i wyobraź sobie to w swojej głowie. Dwuwymiarowa tablica wygląda następująco:
0 Tablica jednowymiarowa Tablica jednowymiarowa
1 Tablica jednowymiarowa
2 Tablica jednowymiarowa
3 Tablica jednowymiarowa
4 Tablica jednowymiarowa
5 Tablica jednowymiarowa
6 Tablica jednowymiarowa
7 Tablica jednowymiarowa
W kodzie:

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
Aby uzyskać wartość 47, musisz odwołać się do elementu macierzy w [4][2].

int number = matrix[4][2];
Być może zauważyłeś, że współrzędne macierzy różnią się od klasycznego prostokątnego układu współrzędnych (kartezjański układ współrzędnych). Gdy uzyskujesz dostęp do macierzy, najpierw określasz współrzędną y, a następnie współrzędną x. W matematyce zwyczajowo podaje się najpierw współrzędną x, czyli (x, y). Być może zastanawiasz się: „Cóż, dlaczego nie obrócić swojej reprezentacji macierzy, a następnie uzyskać dostęp do elementów w zwykły sposób, używając (x, y)? Nie zmieni to zawartości macierzy”. Tak, nic by się nie zmieniło. Ale w świecie programowania przyjętą praktyką jest dostęp do macierzy „najpierw przez y, potem przez x”. Powinieneś zaakceptować to jako właściwy sposób. Porozmawiajmy teraz o rzutowaniu macierzy na nasz silnik (Gameklasa). Jak wiecie, silnik ma wiele metod, które zmieniają komórki pola gry w określonych współrzędnych. Na przykład setCellValue(int x, int y, String value)metoda. Ustawia określoną komórkę o współrzędnych (x, y) równych parametrowi value. Być może zauważyłeś, że ta metoda bierze najpierw x, tak jak w klasycznym układzie współrzędnych. Inne metody silnika działają w podobny sposób. Podczas tworzenia gier często konieczne będzie odtworzenie stanu matrycy na ekranie. Jak to zrobimy? Najpierw musisz przejść przez wszystkie elementy macierzy w pętli. Po drugie, wywołaj metodę wyświetlania dla każdego z nich, używając współrzędnych REVERSED. Na przykład:

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]));
        }
    }
}
Oczywiście odwrócenie działa w obie strony. Możesz przekazać (i, j) do setCellValuemetody i jednocześnie wziąć element [j][i] z macierzy. Odwrócenie współrzędnych może wydawać się nieco trudne, ale trzeba o tym pamiętać. I zawsze, jeśli napotkasz jakieś problemy, powinieneś chwycić kartkę i długopis, narysować matrycę i odtworzyć procesy z udziałem matrycy.

5. Losowe liczby

Jak pracujesz z generatorem liczb losowych? Klasa Gamedefiniuje getRandomNumber(int)metodę. Pod maską wykorzystuje Randomklasę z pakietu java.util, ale sposób pracy z generatorem liczb losowych się nie zmienia. getRandomNumber(int)przyjmuje liczbę całkowitą jako argument. Ta liczba będzie górną granicą tego, co generator może zwrócić. Dolna granica to 0. Ważny! Generator NIGDY nie zwróci górnej wartości granicznej. Na przykład, jeśli wywołasz getRandomNumber(3), losowo zwróci 0, 1 lub 2. Jak widać, nie może zwrócić 3. Korzystanie z generatora w ten sposób jest dość proste, ale w wielu przypadkach bardzo skuteczne. Załóżmy, że potrzebujesz losowej liczby z pewnego zakresu: Wyobraź sobie, że potrzebujesz trzycyfrowej liczby z zakresu [100..999]. Jak już wiesz, minimalna zwracana liczba to 0. Musisz więc dodać 100. Ale w tym przypadku musisz uważać, aby nie przekroczyć górnej granicy. Aby uzyskać 999 jako maksymalną losową wartość, wywołajgetRandomNumber(int)metoda z argumentem 1000. Ale teraz pamiętamy, że do wyniku dodajemy 100: oznacza to, że górna granica powinna zostać zmniejszona o 100. Innymi słowy, kod do uzyskania naszej losowej trzycyfrowej liczby będzie wyglądał następująco :

int number = 100 + getRandomNumber(900);
Aby jednak uprościć tę procedurę, silnik zapewnia getRandomNumber(int, int)metodę, której pierwszym parametrem jest minimalna liczba do zwrócenia. Korzystając z tej metody, poprzedni przykład można przepisać w następujący sposób:

int number = getRandomNumber(100, 1000);
Liczby losowe mogą być użyte do uzyskania losowego elementu tablicy:

String [] names = {"Sarah", "Val", "Sergey"};
String randomName = names[getRandomNumber(names.length)]
Generowanie określonych zdarzeń z pewnym prawdopodobieństwem. Dla ludzi poranki zaczynają się od kilku możliwych scenariuszy: Zaspanie – 50% szans; Obudziłem się na czas – 40% szans; Obudziłem się godzinę wcześniej – 10% szans. Wyobraź sobie, że piszesz poranny generator wyników. Musisz generować zdarzenia z określonym prawdopodobieństwem. Aby to zrobić, ponownie musisz użyć generatora liczb losowych. Możliwe są różne implementacje, ale najprostsza powinna opierać się na następującym algorytmie:
  1. ustawić limity używane do generowania liczby;
  2. wygenerować losową liczbę;
  3. przetworzyć otrzymaną liczbę.
W takim przypadku maksymalna liczba wyniesie 10. Zadzwoń dogetRandomNumber(10)metodę i przeanalizuj, czy możemy ją zwrócić. Może zwrócić 10 liczb (od 0 do 9), każda z takim samym prawdopodobieństwem — 10%. Teraz musimy połączyć wszystkie możliwe wyniki i zmapować je do naszych możliwych zdarzeń. Twoja wyobraźnia może wymyślić wiele możliwych kombinacji, ale oto najbardziej oczywista: „Jeśli liczba losowa mieści się w zakresie [0..4], mamy zdarzenie „Zaspanie”; jeśli liczba mieści się w zakresie [5 ..8], mamy zdarzenie „Obudziliśmy się o czasie”; a jeśli liczba to 9, mamy zdarzenie „Obudziliśmy się godzinę wcześniej”. To wszystko jest bardzo proste. W zakresie [0 ..4], z których każda może zostać zwrócona z prawdopodobieństwem 10%, w sumie 50%; są 4 liczby w przedziale [5..8], no cóż, a 9 to tylko jedna liczba, która pojawia się z prawdopodobieństwo 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");
}
Ogólnie rzecz biorąc, istnieje mnóstwo sposobów na wykorzystanie liczb losowych. Ogranicza Cię tylko Twoja wyobraźnia. Ale są one najskuteczniej wykorzystywane, jeśli chcesz wielokrotnie uzyskać jakiś wynik. Wtedy nowy wynik będzie inny niż poprzedni. Oczywiście z pewnym prawdopodobieństwem. To wszystko na teraz! Jeśli chcesz dowiedzieć się więcej o sekcji „Gry”, oto kilka przydatnych dokumentów, które mogą pomóc:
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION