Nella
sezione "Giochi" di JavaRush troverai progetti entusiasmanti per scrivere giochi per computer popolari. Vuoi creare la tua versione dei popolari "2048", "Sapper", "Snake" e altri giochi? È semplice. Abbiamo trasformato la scrittura del gioco in un processo passo passo.
Per metterti alla prova come sviluppatore di giochi, non devi essere un programmatore avanzato, ma è comunque richiesto un certo insieme di conoscenze Java. Qui troverai
informazioni che ti saranno utili durante la scrittura di giochi .
1. Eredità
Lavorare con il motore di gioco JavaRush implica l'utilizzo dell'ereditarietà. Ma cosa succede se non sai di cosa si tratta? Da un lato, devi capire questo argomento: è studiato al
livello 11 . D'altra parte, il motore è stato volutamente progettato per essere molto semplice, in modo da poter cavarsela con una conoscenza superficiale dell'ereditarietà. Quindi, cos’è l’eredità? Per dirla in modo molto semplice, l’ereditarietà è la relazione tra due classi. Uno di loro diventa il genitore e il secondo diventa il figlio (classe successore). In questo caso, la classe genitore potrebbe anche non sapere di avere classi discendenti. Quelli. non riceve alcun vantaggio particolare dalla presenza di classi eredi. Ma l’ereditarietà offre molti vantaggi a una classe discendente. E la cosa principale è che tutte le variabili e i metodi della classe genitore appaiono nella classe figlia, come se il codice della classe genitore fosse copiato nella classe figlia. Questo non è del tutto vero, ma va bene per una comprensione semplificata dell'ereditarietà. Ecco alcuni esempi per comprendere meglio l'ereditarietà.
Esempio 1: l'eredità più semplice.
public class Родитель {
}
|
La classe Child eredita dalla classe Parent utilizzando la parola chiave extends . |
public class Потомок extends Родитель {
}
|
Esempio 2: utilizzo delle variabili della classe genitore.
public class Родитель {
public int age;
public String name;
}
|
La classe Child può utilizzare le variabili age e name della classe Parent come se fossero dichiarate al suo interno. |
public class Потомок extends Родитель {
public void printInfo() {
System.out.println(name+" "+age);
}
}
|
Esempio 3: utilizzo dei metodi della classe genitore.
public class Родитель {
public int age;
public String name;
public getName() {
return name;
}
}
|
La classe Child può utilizzare le variabili e i metodi della classe Parent come se fossero dichiarati al suo interno. In questo esempio stiamo utilizzando il metodo getName (). |
public class Потомок extends Родитель {
public void printInfo() {
System.out.println(getName()+" "+age);
}
}
|
Questo è l'aspetto della classe
Descendant dal punto di vista del compilatore:
public class Потомок extends Родитель {
public int age;
public String name;
public getName() {
return name;
}
public void printInfo() {
System.out.println(getName()+" "+age);
}
}
2. Metodo prioritario
A volte ci sono situazioni in cui abbiamo ereditato la nostra classe Discendente da una classe Genitore molto utile, insieme a tutte le variabili e i metodi, ma alcuni metodi non funzionano esattamente come vorremmo. O per niente nel modo in cui non vogliamo. Cosa fare in questa situazione? Possiamo ignorare un metodo che non ci piace. Questo viene fatto in modo molto semplice: nella nostra classe Discendente dichiariamo semplicemente un metodo con la stessa firma (intestazione) del metodo della classe Genitore e scriviamo al suo interno il nostro codice.
Esempio 1: override del metodo.
public class Родитель {
public String name;
public void setName (String nameNew) {
name = nameNew;
}
public getName() {
return name;
}
}
|
Il metodo printInfo() stamperà la frase "Luke, No!!!" |
public class Потомок extends Родитель {
public void setName (String nameNew) {
name = nameNew + ",No!!!";
}
public void printInfo() {
setName("Luke");
System.out.println( getName());
}
}
|
Questo è l'aspetto della classe
Descendant dal punto di vista del compilatore:
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());
}
}
Esempio 2: un po' di magia dell'ereditarietà (e dell'overriding del metodo).
public class Родитель {
public getName() {
return "Luke";
}
public void printInfo() {
System.out.println(getName());
}
}
|
public class Потомок extends Родитель {
public getName() {
return "I'm your father, Luke";
}
}
|
In questo esempio: se un metodo
printInfo
(della classe Parent) non viene sovrascritto nella classe Descendant, quando questo metodo viene chiamato su un oggetto della classe Descendant, verrà chiamato il suo metodo
getName()
e non
getName()
la classe Parent.
Родитель parent = new Родитель ();
parent.printnInfo();
|
Questo codice visualizza la scritta "Luke" sullo schermo . |
Потомок child = new Потомок ();
child.printnInfo();
|
Questo codice mostra la scritta "Sono tuo padre, Luke"; . |
Questo è l'aspetto della classe
Descendant dal punto di vista del compilatore:
public class Потомок extends Родитель {
public getName() {
return "I'm your father, Luke";
}
public void printInfo() {
System.out.println(getName());
}
}
3. Elenchi
Se non hai ancora incontrato Lists, ecco una rapida introduzione. Puoi trovare informazioni complete sui
livelli 6-7 del corso JavaRush .
Le liste hanno molto in comune con gli array:
- può memorizzare molti dati di un certo tipo;
- consentire di recuperare gli elementi in base al loro indice/numero;
- gli indici degli elementi iniziano da 0.
Vantaggi degli elenchi: a differenza degli array, gli elenchi possono cambiare dimensione in modo dinamico. Immediatamente dopo la creazione, l'elenco ha una dimensione pari a 0. Man mano che aggiungi elementi all'elenco, la sua dimensione aumenta. Esempio di creazione di un elenco:
ArrayList<String> myList = new ArrayList<String>();
Il valore tra parentesi angolari è il tipo di dati che l'elenco può memorizzare. Ecco alcuni metodi per lavorare con un elenco:
Codice |
Breve descrizione di cosa fa il codice |
ArrayList<String> list = new ArrayList<String>(); |
Creazione di un nuovo elenco di stringhe |
list.add("name"); |
Aggiungi un elemento alla fine dell'elenco |
list.add(0, "name"); |
Aggiunge un elemento all'inizio dell'elenco |
String name = list.get(5); |
Ottieni un elemento tramite il suo indice |
list.set(5, "new name"); |
Cambia l'elemento in base al suo indice |
int count = list.size(); |
Ottieni il numero di elementi in un elenco |
list.remove(4); |
Rimuovere un elemento da un elenco |
Puoi trovare ulteriori informazioni sugli elenchi in questi articoli:
- Classe ArrayList
- ArrayList funzionante nelle immagini
- Rimozione di un elemento da un ArrayList
4. Array
Cos'è una matrice? Una matrice non è altro che una tabella rettangolare che può essere riempita di dati. In altre parole, è un array bidimensionale. Come probabilmente saprai, gli array in Java sono oggetti. Un tipo di array unidimensionale standard
int
si presenta così:
int [] array = {12, 32, 43, 54, 15, 36, 67, 28};
Immaginiamolo visivamente:
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
32 |
43 |
54 |
15 |
36 |
67 |
28 |
La riga superiore indica gli indirizzi delle celle. Cioè, per ottenere il numero 67, devi accedere all'elemento dell'array con indice 6:
int number = array[6];
Qui è tutto molto semplice. Un array bidimensionale è un array di array unidimensionali. Se è la prima volta che ne senti parlare, fermati e immaginalo nella tua testa. Un array bidimensionale assomiglia a questo:
0 |
Matrice unidimensionale |
Matrice unidimensionale |
1 |
Matrice unidimensionale |
2 |
Matrice unidimensionale |
3 |
Matrice unidimensionale |
4 |
Matrice unidimensionale |
5 |
Matrice unidimensionale |
6 |
Matrice unidimensionale |
7 |
Matrice unidimensionale |
Nel codice:
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 |
Per ottenere il valore 47, è necessario accedere all'elemento della matrice in [4][2].
int number = matrix[4][2];
Se noti, le coordinate della matrice sono diverse dal classico sistema di coordinate rettangolari (sistema di coordinate cartesiane).
Quando si accede a una matrice, si specifica prima y e poi x , mentre in matematica è comune specificare prima x(x, y). Potresti chiederti: “Perché non invertire la matrice nella tua immaginazione e accedere agli elementi nel solito modo attraverso (x, y)? Ciò non modificherà il contenuto della matrice.” Sì, non cambierà nulla. Ma nel mondo della programmazione è consuetudine riferirsi alle matrici nella forma “prima y, poi x”. Questo deve essere dato per scontato. Ora parliamo di proiettare la matrice sul nostro motore (classe
Game
). Come sai, il motore ha molti metodi che cambiano le celle del campo di gioco a determinate coordinate. Ad esempio, il
setCellValue(int x, int y, String value)
. Imposta una determinata cella con coordinate (x, y) sul valore
value
. Come hai notato, questo metodo prende innanzitutto esattamente x, come nel sistema di coordinate classico. Il resto dei metodi del motore funzionano in modo simile. Quando si sviluppano giochi, sarà spesso necessario riprodurre lo stato della matrice sullo schermo. Come fare questo? Innanzitutto, in un ciclo è necessario scorrere tutti gli elementi della matrice. In secondo luogo, per ognuno di essi, chiama un metodo da visualizzare con le coordinate INVERTITE. Esempio:
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]));
}
}
}
Naturalmente l’inversione funziona in due direzioni.
setCellValue
Puoi passare (i, j) al metodo , ma allo stesso tempo prendere l'elemento [j][i] dalla matrice. L'inversione può sembrare un po' difficile, ma è una cosa da tenere a mente. E sempre, se sorgono problemi, vale la pena prendere un pezzo di carta con una penna, disegnare una matrice e riprodurre quali processi gli stanno accadendo.
5. Numeri casuali
Come lavorare con un generatore di numeri casuali? La classe
Game
definisce un metodo
getRandomNumber(int)
. Sotto il cofano, utilizza una classe
Random
dal pacchetto java.util, ma ciò non cambia il principio di lavorare con un generatore di numeri casuali.
getRandomNumber(int)
Accetta un numero intero come argomento . Questo numero sarà il limite superiore che il generatore può restituire. Il limite inferiore è 0.
Importante! Il generatore non restituirà MAI un numero limite superiore. Ad esempio, se chiamato
getRandomNumber(3)
in modo casuale può restituire 0, 1, 2. Come puoi vedere, non può restituire 3. Questo utilizzo di un generatore è abbastanza semplice, ma in molti casi molto efficace.
Devi ottenere un numero casuale entro alcuni limiti: immagina di aver bisogno di un numero di tre cifre (100..999). Come già sai, il numero minimo restituito è 0. Quindi dovrai aggiungervi 100. Ma in questo caso devi fare attenzione a non superare il limite superiore. Per ottenere 999 come valore casuale massimo, dovresti chiamare il metodo
getRandomNumber(int)
con un argomento 1000. Ma ricordiamo la successiva aggiunta di 100: ciò significa che il limite superiore dovrebbe essere abbassato di 100. Cioè, il codice per ottenere un numero casuale di tre cifre sarebbe simile a questo:
int number = 100 + getRandomNumber(900);
Ma per semplificare tale procedura, il motore fornisce un metodo
getRandomNumber(int, int)
che accetta come primo argomento il numero minimo da restituire. Utilizzando questo metodo, l'esempio precedente può essere riscritto:
int number = getRandomNumber(100, 1000);
I numeri casuali possono essere utilizzati per ottenere un elemento di un array casuale:
String [] names = {"Andrey", "Валентин", "Сергей"};
String randomName = names[getRandomNumber(names.length)]
Innescare determinati eventi con una certa probabilità. La mattinata di una persona inizia secondo possibili scenari: dormito troppo – 50%; Alzarsi in orario – 40%; Mi sono alzato un'ora prima del previsto – 10%. Immagina di scrivere un emulatore mattutino umano. È necessario attivare eventi con una certa probabilità. Per fare ciò, ancora una volta, è necessario utilizzare un generatore di numeri casuali. Le implementazioni possono essere diverse, ma quella più semplice dovrebbe seguire il seguente algoritmo:
- fissiamo i limiti entro i quali dobbiamo generare il numero;
- generare un numero casuale;
- Elaboriamo il numero risultante.
Quindi, in questo caso, il limite sarà 10. Chiamiamo il metodo
getRandomNumber(10)
e analizziamo cosa può restituirci. Può restituire 10 cifre (da 0 a 9) e ciascuna con la stessa probabilità: 10%. Ora dobbiamo combinare tutti i possibili risultati e abbinarli ai nostri possibili eventi. Possono esserci molte combinazioni, a seconda della tua immaginazione, ma la più ovvia è: "Se un numero casuale è compreso tra [0..4] - attiva l'evento "Slept troppo", se il numero è compreso tra [5.. 8] - “Sveglia” in orario”, e solo se il numero è 9, allora “Mi sono alzato un'ora prima del previsto”. Tutto è molto semplice: all'interno di [0..4] ci sono 5 numeri, ognuno dei quali può ritornare con una probabilità del 10%, che in totale sarà del 50%; all'interno di [5..8] ci sono 4 numeri e 9 è l'unico numero che appare con una probabilità del 10%. Nel codice, tutto questo design intelligente sembra ancora più semplice:
int randomNumber = getRandomNumber(10);
if (randomNumber < 5) {
System.out.println("Проспал ");
} else if (randomNumber < 9) {
System.out.println("Встал вовремя ");
} else {
System.out.println("Встал на час раньше положенного ");
}
In generale, ci possono essere molte opzioni per l'utilizzo di numeri casuali. Tutto dipende solo dalla tua immaginazione. Ma sono utilizzati in modo più efficace se è necessario ottenere risultati ripetutamente. Quindi questo risultato sarà diverso dal precedente. Con una certa probabilità, ovviamente. È tutto! Se vuoi saperne di più sulla sezione Giochi, ecco qualche documentazione utile che può aiutarti:
GO TO FULL VERSION