I
afsnittet "Spil" på CodeGym finder du spændende projekter, der involverer at skrive populære computerspil. Vil du skabe din egen version af de populære spil 2048, Minesweeper, Snake og andre spil? Det er simpelt. Vi har forvandlet spilskrivning til en trin-for-trin proces.
For at teste dine evner som spiludvikler behøver du ikke at være en avanceret programmør, men et specifikt sæt Java-viden er nødvendig. Her finder du
information, der vil være nyttig i spilskrivning .
1. Arv
At arbejde med CodeGym-spilmotoren involverer brug af arv. Men hvad hvis du ikke ved hvad det er? På den ene side skal du forstå dette emne: det studeres på
niveau 11. Til gengæld er motoren specielt designet til at være meget enkel, så man kan slippe afsted med overfladisk viden om arv. Så hvad er arv? Sagt meget enkelt er arv et forhold mellem to klasser. En af dem bliver forælder, og den anden bliver et barn (efterkommer). Desuden ved forældreklassen måske ikke engang, at den har efterkommere. Det opnår med andre ord ikke nogen særlig fordel ved at have efterkommere. Men arv giver en efterkommer mange fordele. Og den vigtigste er, at alle forældreklassens variabler og metoder vises i efterkommerklassen, som om koden for moderklassen blev kopieret til efterkommerklassen. Dette er ikke en helt præcis beskrivelse, men det vil være tilstrækkeligt til en forenklet forståelse af arv.
Eksempel 1: Den enkleste arv.
public class Parent {
}
|
Child - klassen arver Parent- klassen ved hjælp af nøgleordet extends . |
public class Child extends Parent {
}
|
Eksempel 2: Brug af forældreklassens variabler.
public class Parent {
public int age;
public String name;
}
|
Child - klassen kan bruge forældreklassens alders- og navnevariable , som om de var erklæret i forældreklassen . |
public class Child extends Parent {
public void printInfo() {
System.out.println(name+" "+age);
}
}
|
Eksempel 3: Brug af forældreklassens metoder.
public class Parent {
public int age;
public String name;
public getName() {
return name;
}
}
|
Child - klassen kan bruge Parent- klassens variabler og metoder, som om de var erklæret i Child-klassen. I dette eksempel bruger vi metoden getName() . |
public class Child extends Parent {
public void printInfo() {
System.out.println(getName()+" "+age);
}
}
|
Sådan ser
Child- klassen ud for compileren:
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. Overordnede metoder
Nogle gange er der situationer, hvor vi får vores Child-klasse til at arve nogle meget nyttige forældreklasse, sammen med alle dens variabler og metoder, men nogle af metoderne fungerer ikke helt, som vi vil have dem til. Eller slet ikke, som vi vil have dem til. Hvad kan vi gøre i denne situation? Vi kan tilsidesætte den metode, vi ikke kan lide. Dette er meget nemt at gøre: I vores Child-klasse erklærer vi simpelthen en metode med samme signatur som metoden i Parent-klassen, og så skriver vi vores egen kode i den.
Eksempel 1: Tilsidesættelse af en metode.
public class Parent {
public String name;
public void setName(String nameNew) {
name = nameNew;
}
public getName() {
return name;
}
}
|
PrintInfo()-metoden vil vise "Luke, Nej!!!" |
public class Child extends Parent{
public void setName(String nameNew) {
name = nameNew + ", No!!!";
}
public void printInfo() {
setName("Luke");
System.out.println(getName());
}
}
|
Sådan ser
Child- klassen ud for compileren:
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());
}
}
Eksempel 2: Noget arvemagi (og metodetilsidesættelse).
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";
}
}
|
I dette eksempel, hvis
printInfo
metoden (fra Parent-klassen) ikke tilsidesættes i Child-klassen, når denne metode kaldes på et Child-objekt,
getName()
vil dens metode blive kaldt i stedet for Parent-klassens
getName()
metode.
Parent parent = new Parent ();
parent.printnInfo();
|
Denne kode viser "Luke" på skærmen. |
Child child = new Child ();
child.printnInfo();
|
Denne kode viser "Luke, jeg er din far" på skærmen. |
Sådan ser
Child- klassen ud for compileren:
public class Child extends Parent {
public getName() {
return "Luke, I am your father";
}
public void printInfo() {
System.out.println(getName());
}
}
3. Lister
Hvis du endnu ikke har mødt lister (List), er her en kort oversigt. Du kan finde fuldstændig information på niveau
6-7 på CodeGym-kurset .
Lister har meget til fælles med arrays:
- du kan gemme en masse data af en bestemt type;
- de lader dig få varer efter deres indeks;
- elementindeks starter fra 0.
Fordele ved lister: I modsætning til arrays kan lister ændre størrelse dynamisk. Når en liste oprettes, er dens størrelse 0. Når du føjer elementer til en liste, øges dens størrelse. Her er et eksempel på oprettelse af en liste:
ArrayList<String> myList = new ArrayList<String>(); // Create a new ArrayList
Værdien i vinkelparenteserne angiver den datatype, som listen kan gemme. Her er nogle metoder til at arbejde med listen:
Kode |
Kort beskrivelse af hvad koden gør |
ArrayList<String> list = new ArrayList<String>(); |
Opret ny liste over strenge |
list.add("name"); |
Tilføj et element til slutningen af listen |
list.add(0, "name"); |
Tilføj et element til begyndelsen af listen |
String name = list.get(5); |
Få et element ved dets indeks |
list.set(5, "new name"); |
Ændre et element ved dets indeks |
int count = list.size(); |
Få antallet af elementer på listen |
list.remove(4); |
Slet et element fra listen |
Du kan lære mere om lister fra følgende artikler:
- ArrayList klasse
- ArrayList i billeder
- Sletning af et element fra en ArrayList
4. Arrays
Hvad er en matrix? En matrix er ikke andet end en rektangulær tabel, der kan fyldes med data. Med andre ord er det et todimensionelt array. Som du sikkert ved, er arrays i Java objekter. Et standard endimensionelt
int
array ser sådan ud:
int [] array = {12, 32, 43, 54, 15, 36, 67, 28};
Vi kan visualisere det sådan her:
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
32 |
43 |
54 |
15 |
36 |
67 |
28 |
Den øverste række angiver adresserne på celler. Med andre ord, for at få tallet 67, skal du få adgang til array-elementet med indeks 6:
int number = array[6];
Det hele er meget enkelt. Et todimensionelt array er et array af endimensionelle arrays. Hvis du hører om dette for første gang, så stop op og forestil dig det i dit hoved. Et todimensionelt array ser således ud:
0 |
En-dimensionel array |
En-dimensionel array |
1 |
En-dimensionel array |
2 |
En-dimensionel array |
3 |
En-dimensionel array |
4 |
En-dimensionel array |
5 |
En-dimensionel array |
6 |
En-dimensionel array |
7 |
En-dimensionel array |
I kode:
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 |
For at få værdien 47 skal du henvise til matrixelementet ved [4][2].
int number = matrix[4][2];
Du har måske bemærket, at matrixkoordinaterne er forskellige fra det klassiske rektangulære koordinatsystem (kartesiske koordinatsystem).
Når du tilgår matricen, angiver du først y-koordinaten og derefter x-koordinaten. I matematik er det sædvanligt at angive x-koordinaten først, altså (x, y). Du undrer dig måske: "Nå, hvorfor ikke rotere din repræsentation af matricen og derefter få adgang til elementerne på den sædvanlige måde ved hjælp af (x, y)? At gøre dette ville ikke ændre indholdet af matrixen". Ja, intet ville ændre sig. Men i programmeringsverdenen er den accepterede praksis at få adgang til matricer "først af y, derefter med x". Du bør acceptere dette som den rigtige måde. Lad os nu tale om at projicere matrixen til vores motor (
Game
klasse). Som du ved, har motoren mange metoder, der ændrer spillefeltets celler ved bestemte koordinater. For eksempel
setCellValue(int x, int y, String value)
metoden. Den sætter en specifik celle med koordinater (x, y) lig med værdiparameteren. Du har måske bemærket, at denne metode tager x først, ligesom i det klassiske koordinatsystem. Motorens øvrige metoder virker på lignende måde. Når man udvikler spil, vil det ofte være nødvendigt at gengive tilstanden af en matrix på skærmen. Hvordan gør vi det? Først skal du iterere gennem alle matrixelementerne i en løkke. For det andet skal du kalde visningsmetoden for hver af dem ved at bruge REVERSED koordinater. For eksempel:
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]));
}
}
}
Naturligvis virker vendingen i begge retninger. Du kan videregive (i, j) til
setCellValue
metoden og samtidig tage element [j][i] fra matrixen. At vende koordinaterne kan virke lidt svært, men du skal huske det. Og altid, hvis du støder på problemer, bør du tage et stykke papir og en kuglepen, tegne matrixen og gengive processerne, der involverer matrixen.
5. Tilfældige tal
Hvordan arbejder man med en tilfældig talgenerator? Klassen
Game
definerer
getRandomNumber(int)
metoden. Under motorhjelmen bruger den
Random
klassen fra java.util-pakken, men måden du arbejder med tilfældig talgeneratoren på ændrer sig ikke.
getRandomNumber(int)
tager et heltal som et argument. Dette tal vil være den øvre grænse for, hvad generatoren kan returnere. Den nedre grænse er 0.
Vigtig! Generatoren returnerer ALDRIG det øvre grænsetal. For eksempel, hvis du kalder
getRandomNumber(3)
, vil den tilfældigt returnere 0, 1 eller 2. Som du kan se, kan den ikke returnere 3. At bruge generatoren på denne måde er ret simpelt, men meget effektivt i mange tilfælde.
Antag, at du skal have et tilfældigt tal i et eller andet område: Forestil dig, at du har brug for et trecifret tal i området [100..999]. Som du allerede ved, er minimumsantallet returneret 0. Så du skal tilføje 100. Men i dette tilfælde skal du passe på ikke at overskride den øvre grænse. For at få 999 som den maksimale tilfældige værdi, ring til
getRandomNumber(int)
metode med argumentet 1000. Men nu husker vi, at vi tilføjer 100 til resultatet: det betyder, at den øvre grænse skal reduceres med 100. Med andre ord vil koden til at få vores tilfældige trecifrede tal se sådan ud :
int number = 100 + getRandomNumber(900);
Men for at forenkle denne procedure giver motoren metoden,
getRandomNumber(int, int)
hvis første parameter er det mindste antal, der skal returneres. Ved at bruge denne metode kan det foregående eksempel omskrives som følger:
int number = getRandomNumber(100, 1000);
Tilfældige tal kan bruges til at få et tilfældigt array-element:
String [] names = {"Sarah", "Val", "Sergey"};
String randomName = names[getRandomNumber(names.length)]
Generering af visse begivenheder med en vis sandsynlighed. For mennesker begynder morgenen med et par mulige scenarier: Oversovet – 50 % chance; Vågnede til tiden – 40% chance; Vågnede en time for tidligt - 10% chance. Forestil dig, at du skriver en morgenresultatgenerator. Du skal generere begivenheder med en vis sandsynlighed. For at gøre dette skal du igen bruge en tilfældig talgenerator. Forskellige implementeringer er mulige, men den enkleste bør være baseret på følgende algoritme:
- sæt de grænser, der bruges til at generere et nummer;
- generere et tilfældigt tal;
- behandle det opnåede nummer.
I dette tilfælde vil det maksimale være 10. Ring til
getRandomNumber(10)
metode og analysere, at det kan vi vende tilbage. Det kan returnere 10 tal (fra 0 til 9), hver med samme sandsynlighed - 10%. Nu skal vi kombinere alle mulige resultater og kortlægge dem til vores mulige arrangementer. Din fantasi kan tænke på mange mulige kombinationer, men her er den mest oplagte: "Hvis det tilfældige tal er i området [0..4], har vi hændelsen "Overslept"; hvis tallet er i området [5] ..8], har vi begivenheden "Vågnede op til tiden"; og hvis tallet er 9, så har vi begivenheden "Vågnede en time tidligt". Det hele er meget enkelt. Der er 5 tal i området [0] ..4], som hver kan returneres med en sandsynlighed på 10 %, for i alt 50 %; der er 4 tal i området [5..8], ja, og 9 er kun ét tal, der vises med en sandsynlighed på 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");
}
Generelt er der tonsvis af måder at bruge tilfældige tal på. Du er kun begrænset af din fantasi. Men de bruges mest effektivt, hvis du gentagne gange skal have et resultat. Så vil det nye resultat være anderledes end det forrige. Med en vis sandsynlighed, selvfølgelig. Det er alt for nu! Hvis du vil lære mere om afsnittet "Spil", er her noget nyttig dokumentation, der kan hjælpe:
GO TO FULL VERSION