В
раздела „Игри“ на CodeGym ще намерите вълнуващи проекти, които включват писане на популярни компютърни игри. Искате ли да създадете своя собствена version на популярните игри 2048, Minesweeper, Snake и други игри? Просто е. Превърнахме писането на игри в процес стъпка по стъпка.
За да тествате способностите си като разработчик на игри, не е нужно да сте напреднал програмист, но са необходими специфични познания по Java. Тук ще намерите
информация, която ще бъде полезна при писането на игри .
1. Наследство
Работата с двигателя на играта CodeGym включва използване на наследяване. Но Howво, ако не знаете Howво е това? От една страна, трябва да разберете тази тема: тя се изучава на
ниво 11. От друга страна, двигателят е специално проектиран да бъде много прост, така че можете да се измъкнете с повърхностни познания за наследството. И така, Howво е наследство? Казано много просто, наследяването е връзка между два класа. Единият от тях става родител, а другият става дете (потомък). Освен това родителският клас може дори да не знае, че има потомци. С други думи, не печели няHowво особено предимство, като има потомци. Но наследяването дава на потомъка много предимства. И най-важното е, че всички променливи и методи на родителския клас се появяват в потомъка, сякаш codeът на родителския клас е копиран в наследствения клас. Това не е съвсем точно описание, но ще бъде достатъчно за опростено разбиране на наследството.
Пример 1: Най-простото наследяване.
public class Parent {
}
|
Класът Child наследява класа Parent с помощта на ключовата дума extends . |
public class Child extends Parent {
}
|
Пример 2: Използване на променливите на родителския клас.
public class Parent {
public int age;
public String name;
}
|
Класът Child може да използва променливите за възраст и име на класа Parent, сякаш са декларирани в класа Parent . |
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;
}
}
|
Класът Child може да използва променливите и методите на класа Parent , сякаш са декларирани в класа Child. В този пример използваме метода getName() . |
public class Child extends Parent {
public void printInfo() {
System.out.println(getName()+" "+age);
}
}
|
Ето How изглежда класът
Child за компилатора:
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. Основни методи
Понякога има ситуации, в които караме нашия клас Child да наследи някой много полезен клас Parent, заедно с всички негови променливи и методи, но някои от методите не работят точно Howто искаме. Или изобщо не Howто искаме. Какво можем да направим в тази ситуация? Можем да отменим метода, който не ни харесва. Това се прави много лесно: в нашия клас Child ние просто декларираме метод със същата сигнатура като метода в класа Parent и след това пишем собствен code в него.
Пример 1: Замяна на метод.
public class Parent {
public String name;
public void setName(String nameNew) {
name = nameNew;
}
public getName() {
return name;
}
}
|
Методът printInfo() ще покаже „Лука, не!!!“ |
public class Child extends Parent{
public void setName(String nameNew) {
name = nameNew + ", No!!!";
}
public void printInfo() {
setName("Luke");
System.out.println(getName());
}
}
|
Ето How изглежда класът
Child за компилатора:
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: НяHowва магия за наследяване (и отмяна на метод).
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
методът (от класа Parent) не е заменен в класа Child, когато този метод се извика на обект Child, неговият
getName()
метод ще бъде извикан instead of
getName()
метода на класа Parent.
Parent parent = new Parent ();
parent.printnInfo();
|
Този code показва "Лука" на екрана. |
Child child = new Child ();
child.printnInfo();
|
Този code показва на екрана „Лука, аз съм твоят баща“ . |
Ето How изглежда класът
Child за компилатора:
public class Child extends Parent {
public getName() {
return "Luke, I am your father";
}
public void printInfo() {
System.out.println(getName());
}
}
3. Списъци
Ако все още не сте срещали списъци (List), ето кратък преглед. Можете да намерите пълна информация в нива
6-7 на курса CodeGym .
Списъците имат много общо с масивите:
- можете да съхранявате много данни от определен тип;
- те ви позволяват да получавате елементи по техния индекс;
- Индексите на елементите започват от 0.
Предимства на списъците: За разлика от масивите списъците могат да променят размера си динамично. Когато се създаде списък, размерът му е 0. Докато добавяте елементи към списък, размерът му се увеличава. Ето пример за създаване на списък:
ArrayList<String> myList = new ArrayList<String>(); // Create a new ArrayList
Стойността в ъгловите скоби показва типа данни, които списъкът може да съхранява. Ето някои методи за работа със списъка:
Код |
Кратко описание на това Howво прави codeът |
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); |
Изтриване на елемент от списъка |
Можете да научите повече за списъците от следните статии:
- Клас ArrayList
- ArrayList в снимки
- Изтриване на елемент от ArrayList
4. Масиви
Какво е матрица? Матрицата не е нищо повече от правоъгълна table, която може да бъде попълнена с данни. С други думи, това е двуизмерен масив. Както вероятно знаете, масивите в 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 |
Най-горният ред показва addressите на клетките. С други думи, за да получите числото 67, трябва да получите достъп до елемента на масива с индекс 6:
int number = array[6];
Всичко е много просто. Двумерният масив е масив от едномерни масиви. Ако чувате за това за първи път, спрете и си го представете в главата си. Двуизмерният масив изглежда така:
0 |
Едномерен масив |
Едномерен масив |
1 |
Едномерен масив |
2 |
Едномерен масив |
3 |
Едномерен масив |
4 |
Едномерен масив |
5 |
Едномерен масив |
6 |
Едномерен масив |
7 |
Едномерен масив |
В code:
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, точно Howто в класическата координатна система. Другите методи на двигателя работят по подобен начин. При разработването на игри често ще е необходимо да се възпроизведе състоянието на матрица на екрана. Как да направим това? Първо, трябва да преминете през всички матрични елементи в цикъл. Второ, извикайте метода за показване за всеки от тях, като използвате REVERSED координати. Например:
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] от матрицата. Обръщането на координатите може да изглежда малко трудно, но трябва да го запомните. И винаги, ако срещнете няHowъв проблем, трябва да вземете лист хартия и химикал, да начертаете матрицата и да възпроизведете процесите, включващи матрицата.
5. Случайни числа
Как работите с генератор на произволни числа? Класът
Game
определя
getRandomNumber(int)
метода. Под капака той използва
Random
класа от пакета java.util, но начинът, по който работите с генератора на произволни числа, не се променя.
getRandomNumber(int)
приема цяло число като аргумент. Това число ще бъде горната граница на това, което генераторът може да върне. Долната граница е 0.
важно! Генераторът НИКОГА няма да върне числото на горната граница. Например, ако извикате
getRandomNumber(3)
, той произволно ще върне 0, 1 or 2. Както можете да видите, не може да върне 3. Използването на генератора по този начин е доста просто, но много ефективно в много случаи.
Да предположим, че трябва да получите произволно число в няHowъв диапазон: Представете си, че имате нужда от трицифрено число в диапазона [100..999]. Както вече знаете, минималният върнат брой е 0. Така че ще трябва да добавите 100. Но в този случай трябва да внимавате да не превишите горната граница. За да получите 999 като максимална произволна стойност, извикайте
getRandomNumber(int)
метод с аргумента 1000. Но сега си спомняме, че добавяме 100 към резултата: това означава, че горната граница трябва да бъде намалена със 100. С други думи, codeът за получаване на нашето случайно трицифрено число ще изглежда така :
int number = 100 + getRandomNumber(900);
Но за да опрости тази proceduresа, двигателят предоставя метод,
getRandomNumber(int, int)
чийто първи параметър е минималният брой за връщане. Използвайки този метод, предишният пример може да бъде пренаписан, Howто следва:
int number = getRandomNumber(100, 1000);
Случайни числа могат да се използват за получаване на произволен елемент от масив:
String [] names = {"Sarah", "Val", "Sergey"};
String randomName = names[getRandomNumber(names.length)]
Генериране на определени събития с известна вероятност. За хората сутрините започват с няколко възможни сценария: Проспали – 50% шанс; Събуди се навреме – 40% шанс; Събудих се един час по-рано – шанс 10%. Представете си, че пишете генератор на сутрешни резултати. Трябва да генерирате събития с определена вероятност. За да направите това, отново трябва да използвате генератор на произволни числа. Възможни са различни реализации, но най-простата трябва да се основава на следния алгоритъм:
- задайте ограниченията, използвани за генериране на число;
- генериране на случайно число;
- обработете полученото число.
В този случай максимумът ще бъде 10. Обадете се на
getRandomNumber(10)
метод и анализираме, че можем да го върнем. Може да върне 10 числа (от 0 до 9), всяко с еднаква вероятност — 10%. Сега трябва да комбинираме всички възможни резултати и да ги съпоставим с нашите възможни събития. Вашето въображение може да измисли много възможни комбинации, но ето най-очевидната: „Ако произволното число е в диапазона [0..4], имаме събитието „Проспали“; ако числото е в диапазона [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");
}
Като цяло има много начини за използване на произволни числа. Вие сте ограничени само от вашето въображение. Но те се използват най-ефективно, ако трябва многократно да получите няHowъв резултат. Тогава новият резултат ще бъде различен от предишния. С известна вероятност, разбира се. Това е всичко за сега! Ако искате да научите повече за раздела „Игри“, ето полезна documentация, която може да ви помогне:
GO TO FULL VERSION