Как написать свою игру на java

Время на прочтение
5 мин

Количество просмотров 142K

Доброго времени суток всем!

В этой статье будет описываться создание 2D игры на Java. Сразу предупреждаю, вы должны хотя бы базово знать язык Java, поскольку на подробное объяснение каждой строки у меня нету времени. И очень прошу вас, не списывать просто код, а пытаться понять что означает каждая строка, и писать со смыслом. И еще, я использую Eclipse, но вы можете использовать любой IDE.

Задача:

Я планирую создать игру, напоминающую шутер с видом от 3 лица.

Начало:

Ну что, приступим!

Для начала создадим проект. Назовем его «Just game». И сразу создаем класс Display.java. В него пишем:

public static void main(String[] args) {
		JFrame frame = new JFrame(/* название нашей игры */);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
		frame.setUndecorated(true);
		frame.setVisible(true);
	}

Теперь разберемся, что мы сделали.

JFrame frame = new JFrame(/*название нашей игры*/);

мы создаем рамку, которая и будет отображаться при запуске нашей игры

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

устанавливаем операцию, которая будет происходить при нажатии на крестик. EXIT_ON_CLOSE — выйти из программы

frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setUndecorated(true);

устанавливаем нашей рамке максимальные размеры, убираем декорации(кнопки свернуть, закрыть, уменьшить/увеличить и т.п.), т.е. делаем игру на весь экран. Если вы хотите, чтобы игра не была бы на весь экран, то используйте:

frame.setSize(/*ширина*/,/*высота*/);
frame.setResizable(false); //false чтобы нельзя было бы поменять размеры рамки, true -можно

frame.setVisble(true);

— делаем рамку видимой

Только не забудьте, все настройки рамки надо писать до того, как вы сделаете её видимой

Ну чтож, теперь нажимаем «Run» и пробуем запустить нашу игру. Если все написано правильно, у вас не должны возникать ошибки и должно появиться пустое, серое окно.

Серое окно… Как скучно… Давайте создадим что-нибудь поинтереснее.

Создадим новый класс, под названием «Main». Main класс у нас будет являться панелью, которую мы вставим в рамку, по этому он должен расширять JPanel. (Для тех, кто не знает, расширять пишется как extends после названия класса)

Возвращаемся в класс Display и после настроек рамки, но перед установлением её видимости, пишем:

frame.add(new Main());

Вы спросите — «Ну и зачем мы это сделали?». Представьте себе картину. Эта картина и является конечная наша игра. А теперь представьте рамку. Без ничего внутри, просто пустую рамку. На ней ничего нельзя нарисовать, она бесполезна. Для этого, мы вставили в картину пустой лист, на котором программа в дальнейшем может рисовать картину. На этом закончим наше лирическое отступление и вернемся к классу Main.

Нам нужно осуществить отрисовку, по этому мы должны добавить метод paint. Для этого пишем:

public void paint(Graphics g) {
//отрисовка всех объектов
}

Ну и для начала, можем написать внутри этого метода отрисовку линии. Для этого пишем:

g.drawLine(20, 20, 100, 100);

Теперь запускаем программу, и видим:

image

Даааааа, не густо…

Давайте отрисуем какую-нибудь картинку. Например эту:

image

Для начала, нам нужно указать путь к картинке. Для этого не в методе paint, пишем:

Image img = new ImageIcon("2.png").getImage();

(предварительно надо в наш проект скинуть картинку и назвать ее 2.png)

После этого удаляем строчку отрисовки линии, а вместо нее в метод paint пишем:

g.drawImage(img, 0, 0, null);

Разберемся поближе с методом drawImage, так как мы будем часто его затрагивать.

drawImage(картинка которую мы будем рисовать, которую мы объявили раннее, координата X с которой будет рисоваться картинка, координата Y с которой будет рисоваться картинка, paint);

Отдельно хочу поговорить о параметре paint. Лучше всего оставляйте его null. Я только однажды сталкивался, когда мне нужно было использовать paint. Это было когда я отрисовывал текст, и задавал ему размер шрифта. Но советую не лезть туда и использовать null.

Теперь запускаем программу, и видим:

image

Чего-то она маленькая, не правда ли? Давайте научимся увеличивать её размеры. Добавляем к drawImage() параметры так, чтобы вышло:

g.drawImage(img, 0, 0, 1920, 1080, null);

Что мы сейчас добавили? Эти два параметра растягивают картинку, до координат 1920 и 1080. Получается, что картинка на весь экран. Давайте запустим программу и это проверим.

Получается:

image

Ну наконец-то. Теперь мы умеем любые картинки растягивать на весь экран. Но вот проблема. Метод paint вызывается только один раз. И как же его обновлять постоянно? Для этого существует очень полезная вещь — таймер. Давайте создадим его.

Для этого пишем:

Timer timer = new Timer(20, this);

(20 это частота с которой обновляется таймер, this- где выполнять метод при обновлении таймера
Это мы должны вписать сразу после строки определения класса, т.е. после:

public class Main extends JPanel{

Также, надо дополнить строку определения класса таким образом:

public class Main extends JPanel implements ActionListener{

После прописывания этой строки, у вас название класса должно подчеркнуться красным. Чтобы это исправить, в самом конце класса добавьте метод:

@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		
	}
	

Этот метод будет выполняться при обновлении таймера. В него мы должны написать repaint(); чтобы при каждом обновлении таймера у нас все элементы бы стирались, и нарисовывались заново.

Дальше, мы должны запустить таймер. Для этого, создаем конструктор класса Main и в него пишем:

timer.start();

После этого, можете не запускать программу, ведь в ней ничего не изменится. Давайте заменим текстуру домика на нормальную текстуру карты. Её вы можете нарисовать сами, либо скопировать у меня пробную:

image

Размер картинки может быть любой, все равно её размер будет подгоняться прямо в программе. Ах да, разрешения компьютеров могут быть разные, так что добавим-ка в конструктор такие вещи:


public Main(JFrame frame) {
timer.start();
this.frame = frame;
}

И перед конструктором добавим:

JFrame frame;

И сходим еще в класс Display.java и там немного изменяем метод frame.add:

frame.add(new Main(frame));

Таким образом, наша рамка будет передаваться в класс Main.java. Переходим в этот класс, и там где у нас метод paint() меняем строку drawImage() на:

g.drawImage(img, 0, 0,frame.getWidth(), frame.getHeight(), null);

Таким образом, теперь наша игра будет отрисовывать картинку на весь экран, в независимости от его разрешения. Запускаем:

image

На сегодня все. Оставляю код, для тех, кто запутался:

Display.java

import javax.swing.JFrame;

public class Display {
	
	public static void main(String[] args) {
		JFrame frame = new JFrame("JustGame");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
		frame.setUndecorated(true);
		frame.add(new Main(frame));
		frame.setVisible(true);
	}
	
}

Main.java

import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Main extends JPanel implements ActionListener{
	
	Image img = new ImageIcon("2.png").getImage();
	
	Timer timer = new Timer(20, this);
	
	JFrame frame;
	
	public Main(JFrame frame) {
		this.frame = frame;
	}
	
	public void paint(Graphics g) {
		g.drawImage(img, 0, 0,frame.getWidth(), frame.getHeight(), null);
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		repaint();
	}
	
}

Спасибо за внимание!

Продолжение habrahabr.ru/post/326302

Немного о Java

Java — это не только язык, это целая экосистема, включающая в себя средства разработки, платформу для запуска готовых приложений, огромный свод документации и активное сообщество. Одним из преимуществ Java на начальном этапе была кросс-платформенность (принцип — «написано один раз — запускается везде»).

Дело в том, что программа на Java исполняется не на прямую процессором компьютера, а виртуальной машиной Java (JVM). Это позволяет абстрагироваться от многих нюансов конкретных платформ. Программу, написанную на Java, можно без изменений кода запускать на Windows, Linux, MacOS и других операционных системах (если, конечно, программа не использует специфичные для ОС функции).

Кто застал начало 2000х, наверное помнит огромное количество мобильных телефонов (тогда еще они не были смартфонами), на каждом телефоне была по сути своя маленькая ОС, но при этом почти на каждом можно было запустить Java игру или приложение.

На сегодняшний день Java по-прежнему входит в топ языков для изучения, а Java как платформа — в топ используемых технологий в мире IT и смежных областях.

Создание проекта, первые шаги

Сегодня мы начнем изучать Java, причем сразу с примера игры «Крестики-нолики».

Итак, поехали. Надеюсь как установить java SDK ты уже разобрался. Мы будем писать код в IDE IntelliJ IDEA, но если у вас какая-то другая, например Eclipse, то разницы большой не будет.

Итак, создаем новый проект: нажимаем «create new project», выбираем java и щелкаем «next» до окна, где требуется ввести имя проекта, вводим TicTacToe (крестики-нолики). В некоторых случаях на этапе создания потребуется выбрать шаблон проекта, тогда смело выбирай что-либо похожее на JavaConsoleApplication.

После этого нажимаем «Finish». Idea немного подумает и сгенерирует нам проект с классом Main, в котором определена функция main().

Давайте разберемся, что здесь что. Слева открыто окно структуры проекта «Project», как мы видим в папке src в пакете com.company находится единственный java-файл нашей программы с именем Main. Справа показано его содержимое. Тут надо сделать небольшое отступление, дело в том, что в Java почти все представлено классами. В том числе и файлы программы описывают классы, причем имя файла должно совпадать с классом, который описывается в этом файле (например, у нас файл Main.java описывает класс Main). Пусть слово «класс» не смущает на первом этапе. Пока лишь отмечу, что для глубокого изучения Java так или иначе придется познакомиться с объектно-ориентированным подходом. В двух словах, класс можно воспринимать как шаблон, идею, а экземпляры класса — как реализацию этой идеи. Экземпляры класса называются его объектами. Например, вы можете представить идею стола (нечто, на что можно класть предметы), однако конкретных экземпляров такого стола огромное множество (на одной ножке, на четырех, круглые, квадратные, из разных материалов). Примерно так соотносятся классы и объекты в объектно-ориентированном программировании.

Внутри нашего класса Main описана функция main(), в Java с этой функции начинается исполнение программы, это точка входа в наше приложение. Сейчас там написан только автоматический комментарий (комментарии в Java начинаются с двух символов //). Попробуем кое-что добавить в наш код и проверить работоспособность приложения. Внутри функции main() допишем две строки:

Встроенная функция println() просто выводит на экран текстовую информацию. Запустим наше приложение (нажимаем shift-F10 или зеленый треугольник). Внизу, во вкладке run появится вывод нашей программы:

Функция main() отработала и закончилась, вместе с ней закончилась наша программа.

В игре пользователю конечно захочется взаимодействовать с программой более продвинутым способом, поэтому нам понадобится окно. Набираем внутри функции main() следующие строки:

Смысл большинства строк понятен из комментариев к ним, отдельно отмечу строку window.setLayout() — здесь устанавливается менеджер расположения, который будет применяется к компонентам, добавляемым в наше окно. Менеджер BorderLayout может располагать новые компоненты относительно сторон света (North(верх), West(слева), East(справа), South(низ)), Center (центр)). По умолчанию он располагает компоненты по центру. Подробнее с менеджерами расположения можно познакомиться в документации.

Теперь, если запустить нашу программу, мы увидим окно:

Пока в этом окне ничего нет. Создадим свой компонент, который и будет отрисовывать графику игры.

Свой компонент для рисования

Очевидно, что рисовать в консоли у нас не получится, нужен какой-то компонент для более продвинутого взаимодействия с пользователем. Для этой цели создадим еще один класс, назовем его TicTacToe. Щелкаем правой клавишей мыши на имени пакета приложения (в данном случае это com.company)

И в появившемся меню выбираем пункт «New» → «Java Class». В окне создания класса набираем его имя «TicTacToe» и нажимаем «Enter».

У нас в проекте появился еще один класс. В главное окно можно добавлять только объекты класса JComponent, кроме того, нам нужна область для рисования. Поэтому наследуем наш класс TicTacToe от JComponent. Ой сколько непонятных слов! Сейчас постараюсь пояснить.

Наследование классов — это как создание уточненного шаблона на базе существующего. Например, есть класс Стол, описывающий идею стола вообще. Но нам очень часто приходится создавать столы на четырех ногах с деревянной столешницей, поэтому для удобства мы можем уточнить идею класса Стол и создать шаблон ДеревянныйСтол — он будет иметь все те же основные свойства, что и родительская идея, но зато часть свойств у него уже определены и понятны — это число ног, равное четырем и материал столешницы — дерево.

С JComponent то же самое — данный класс реализует идею некоторого графического компонента пользовательского интерфейса. Такой компонент можно добавить в окно и он умеет как-то себя отрисовывать. Например, класс JButton — наследник класса JComponent, это компонент, который выглядит, как кнопка и умеет показывать анимацию клика мышкой.

Здесь же, наследуя класс JComponent, мы создадим свой компонент, в котором сможем рисовать то, что нам нужно.

Итак дописываем extends JComponent в строку описания класса:

Слово extends говорит о том, что наш класс TicTacToe расширяет (наследует) класс JComponent.

У всех компонентов есть метод paintComponent(), который отвечает за их отрисовку. В параметры этого метода приходит объект Graphics, с помощью которого мы и будем рисовать то, что нам необходимо. Давайте переопределим метод paintComponent так, чтобы он рисовал окружность (это необязательно, но для проверки, что у нас все работает как надо, будет хорошим тоном это сделать).

Переопределим метод paintComponent() в классе TicTacToe следующим образом:

метод setColor() объекта graphics, как очевидно из названия, устанавливает цвет, которым мы будем рисовать, а метод drawOval(x, y, w, h) — в общем случае рисует овал с координатами центра x, y, шириной — w и высотой h. В данном случае рисуется окружность, так как ширина и высота заданы одинаковые — 100. Замечу, что экранные координаты отсчитываются от левого верхнего угла. То есть 0 по вертикали находится вверху.

Чтобы проверить, как выглядит наш объект класса TicTacToe надо создать его экземпляр и добавить в главное окно в качестве дочернего компонента. Создание новых объектов в Java осуществляется с помощью ключевого слова new.

Например, если у нас есть класс Стол и мы хотим создать объект этого класса (настоящий конкретный стол), то мы должны написать что-то такое: стол = new Стол(). Здесь «стол» имя, по которому мы будем обращаться к нашему объекту (взаимодействовать с ним), а Стол — имя класса, объект которого мы создаем.

Замечу сразу, что вместо «стол» мы могли написать любое имя, например «fdgdgdgfd», но программисты обычно стараются давать «говорящие» имена объектам, чтобы код было легче читать. Чтобы создать экземпляр класса TicTacToe мы можем также написать game = new TicTacToe(), а потом добавить его в окно методом add().

Теперь код класса Main выглядит вот так:

Если теперь запустить нашу программу, то мы увидим окно с окружностью:

Ну что ж. Рисовать в базе мы научились. Время приступать к созданию игры.

Создание игрового поля

Вернемся к классу TicTacToe. Для начала необходимо нарисовать игровое поле, состоящее из девяти клеточек. Для этого давайте нарисуем две горизонтальные и две вертикальные линии на нашем поле. Чтобы это сделать, воспользуемся методом drawLine(x1,y1,x2,y2) объекта Graphics, который приходит к нам в метод paintComponent() в качестве параметра. Метод drawLine() рисует линию от точки с координатами x1,y1 до точки x2,y2. Давайте подумаем как нарисовать игровое поле.

Если мы разобьем высоту поля на три (у нас же три клетки в ряду), то получим высоту одной клетки (назовем ее dh). Узнать высоту всего компонента можно методом getHeight(). Значит, мы должны нарисовать первую горизонтальную линию от точки 0,dh до точки w, dh, где w — ширина поля. Но это только одна горизонтальная линия, вторую рисуем также, но координаты будут уже: начало — 0, 2*dh, конец w, 2*dh. По аналогии, если высота поля равна h, а ширина одной клетки равна dw, то вертикальные линии рисуются в координатах d, 0 — d, h и dw*2, 0 — dw*2, h.

Теперь давайте немного поговорим о переменных. Если помните — в алгебре за буквой могло скрываться какое-то значение, например выражение x = 2*a, подразумевало, что на место буквы а можно подставить любое значение и вычислить x.

Примерно то же самое происходит с переменными в программировании. Имя переменной (идентификатор) сопоставлен с некоторым значением и «хранит» его «в себе» (на самом деле, с объектами классов все несколько сложнее, там мы имеем дело со ссылками, но это пока за рамками данного материала).

Помимо этого, в программах есть разные типы данных. Наверное, вы согласитесь, что строку и целое число надо хранить в памяти как-то по-разному? Даже целые и дробные числа требуют разного подхода, поэтому в программах данные соответствуют определенным типам. В нашем примере нам уже понадобились значения ширины и высоты ячейки игрового поля dw и dh. Чтобы вычислить и сохранить их значения в памяти, воспользуемся следующими выражениями:

Здесь int — означает тип данных «целое число». Выражение int a = 10 объявляет переменную с именем a и задает ей сразу значение 10. В нашем примере создаются четыре переменных, значения w и h получаются из методов самого компонента TicTacToe, а dw и dh вычисляются. Обратите внимание, что при делении w / 3 получается целый тип данных. В Java, как и во многих других языках, деление целого на целое дает в результате целое. При этом дробная часть просто отбрасывается (округления нет). Заметьте, что здесь не используется слово «new», так как создаются не объекты, а переменные простых (скалярных) типов данных, в данном случае типа int.

Мы могли бы уже написать код для рисования всех линий, но мы же программисты, а программисты любят все упрощать, правда для этого они пишут много дополнительного кода. Представим, что у нас было бы поле не 3 на 3 клетки а, например, 15х15. Как бы мы его разлиновали? Вручную набирать код для рисования 28 линий это уж слишком.

К счастью, во всех языках программирования (привет ассемблер) есть конструкции, позволяющие повторить заданное число раз тот или иной участок кода — циклы.

Разберем, как автоматизировать рисование линий, заметим, что все горизонтальные линии содержат одни и те же значения координат по горизонтали (от начала до конца ширины игрового поля), при этом их координаты по вертикали различаются на dh. У первой линии высота dh, у второй 2*dh, и так далее.

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

Попробуем рисовать линии с помощью цикла, в классе TicTacToe создадим свой метод с названием drawGrid(), он будет у нас отвечать за рисование линий сетки игрового поля:

Еще раз пробежимся по коду. Первые четыре строки метода — необходимые нам значения ширины, высоты игрового поля и ширины, высоты одной ячейки. Цикл начинается с ключевого слова for, в скобках после него указывается переменная, которая будет счетчиком (у нас она еще и объявляется сразу int i = 1), условие при ложности которого цикл прервется и выражение изменяющее переменную-счетчик (i++ увеличивает i каждую итерацию цикла на единицу).

Внутри цикла каждую итерацию рисуются очередные горизонтальная и вертикальная линии поля.

Добавим вызов нашего метода drawGrid() в метод отрисовки всего компонента paintComponent():

Запускаем программу и видим разрисованное поле:

Скажи мне, куда ты кликнул?

Итак, наше игровое поле выглядит готовым к игре, но теперь нам надо узнать в какой из квадратов кликнул пользователь. Для этого давайте немного настроим наш компонент TicTacToe, чтобы он смог принимать события от мыши. Во-первых, нам необходимо включить получение таких событий. Делается это с помощью метода enableEvents(), но где его вызвать?

Конечно можно было бы добавить его вызов в наш метод drawGrid() или даже в paintComponent(), но эти методы по логике работы игры будут вызываться каждый раз, когда мы захотим что-то нарисовать. А включить события надо лишь один раз. Где бы найти метод, который вызывается у компонента единожды, например при его создании?

На самом деле такой метод есть у каждого класса и называется он конструктором. Именно конструктор вызывается при попытке создания нового объекта. Конструкторов может быть несколько, он так же как обычный метод может принимать параметры, но вот возвращаемого значения у него нет. Конструктор класса имеет то же имя, что и класс. Создадим конструктор в классе TicTacToe:

Как видим — ничего сложного, просто еще один метод. А как же наш компонент создавался до этого? Ведь в классе Main мы его уже создавали. Помните, game = new TicTacToe()? Тут тоже никакой магии — если конструктор не задан явно, используется конструктор по умолчанию.

Именно здесь мы включим получение событий от мыши:

Хорошо! Получение событий мы включили, а где же мы их будем получать? В методе processMouseEvent() конечно, именно он будет срабатывать каждый раз, когда указатель мыши каким-либо образом взаимодействует с нашим игровым полем.

Приведу на всякий случай полный код класса TicTacToe на текущий момент:

Мозг игры

Ну не то чтобы уж мозг, но некоторую начинку нам создать придется. Итак, давайте подумаем, как хранить состояние игры? Неплохо бы было хранить состояние каждой клетки игрового поля, для этой цели хорошо подойдет двумерный массив целых чисел размером 3х3. Создается он просто int[][] field = new int[3][3].

Массив это уже целый объект, на который выделяется память в отдельной области (куче), поэтому тут мы используем слово new. Создадим в классе TicTacToe новый метод под названием initGame(), он будет отвечать у нас за сброс игры к начальному состоянию, именно здесь мы будем «очищать» игровое поле.

Для хранения состояния ячейки поля создадим три константы со значениями 0, 10 и 200. Ноль будет соответствовать пустой ячейке, 10 — крестику, а 200 — нолику. Первоначально заполним массив нулями.

Теперь добавим немного кода в метод processMouseEvent(), который отвечает за клики мышью на игровом поле. Также заведем логическую переменную isXturn, которая будет показывать чей сейчас ход.

Пробежимся по коду. Переход от координат к индексам довольно прост: мы делим текущую координату на размер одной ячейки и получаем сколько целых ячеек укладывается до текущей (если совсем непонятно, то поясню: разделить на w/3 это то же самое, что умножить на 3/w).

В 42й строке кода стоит условный оператор (также называемый ветвлением), пора с ним познакомиться. Если условие в скобках истинно (в нашем случае если поле пустое), то мы заходим «внутрь» условного оператора (строки 43-46), если же условие ложно (ячейка уже занята), то мы пройдем дальше. Что же происходит если кликнутая ячейка пуста?

В 44й строке после «=» стоит еще один интересный оператор — тернарный, он дает возможность записать в строку ветвление, если в результате него присваивается значение. Записывают его так: isXturn? — это проверка, чей сейчас ход (ходит крестик, если значение «истина»), далее следует определенная нами константа FIELD_X, именно она будет результатом выражения, если isXturn — true.

После FIELD_X стоит двоеточии и константа FIELD_O — ее значение станет результатом выражения, если ход «нолика». После изменения значения в ячейке массива, меняем очередность хода: isXturn =! isXturn изменит значение переменной на противоположное. В конце всех действий — вызываем перерисовку компонента, так как теперь нужно нарисовать крестик или нолик, там где его не было раньше.

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

Комментарии в коде достаточно очевидны. Коротко поясню, что крестик мы рисуем как пересечение двух линий из угла в угол ячейки, а нолик — как овал чуть вытянутый по вертикали. Теперь у нас есть методы, рисующие крестик и нолик по заданным индексам ячейки поля. Как же мы будем рисовать процесс игры? Пробежимся еще раз по коду. Игроки кликают мышкой на наш компонент, при этом срабатывает метод processMouseEvent(), в котором мы определяем, какое событие произошло, пуста ли ячейка, в которую кликнули и вызываем перерисовку компонента (repaint()). На момент перерисовки в массиве field содержатся актуальные данные о поставленных крестиках и ноликах, остается пробежаться циклами по всему массиву и если встречается нолик — рисовать нолик, а если крестик — рисовать крестик. Поехали, создаем метод drawXO(). Именно в нем будем «просматривать» массив:

Осталось вызвать данный метод в методе painComponent():

Теперь, если запустить нашу программу можно понаслаждаться постановкой крестиков и ноликов:

Определяем победителя

Все бы хорошо, но сейчас игра никак не отслеживает свое состояние, то есть крестики и нолики успешно ставятся, но выигрыш никак не определяется. Придется еще немного потрудиться! Как же нам определить, что игра закончилась? Давайте присмотримся к массиву field. Если в ряду будут стоять одни крестики, значит, там значения 10, 10, 10.

Если нолики — 200, 200, 200. Эврика! Давайте проверять сумму всех ячеек в ряду по горизонтали и вертикали, а также по двум диагоналям. Создаем еще один метод checkState(), он будет каждый ход проверять сложившуюся на поле ситуацию и возвращать -1, если ходов не осталось, 3*FIELD_X если выиграли крестики и 3*FIELD_O, если выиграли нолики, в случае продолжения игры — метод пусть вернет 0.

Основные комментарии даны в коде. Элементы, стоящие на главной диагонали вычисляются просто — их индексы равны ([0][0], [1][1] и т. д.). Побочная диагональ содержит элементы с индексами [0][N-1], [1][N-2] и так далее (N — длина массива).

Часть кода с 142й по 160ю строку отвечает за подсчет суммы значений в ячейках по вертикальным и горизонтальным рядам: каждую «большую» итерацию по i фиксируется вертикальный (горизонтальный) ряд с индексом i и запускается малый цикл с перебором всех ячеек в данном ряду (цикл по j).

Кроме того, проверяется наличие на поле хотя бы одной не занятой ячейки (hasEmpty=true), это нужно чтобы определить ситуацию, когда все ячейки заняты, но никто не выиграл (ничья). Наконец, если нигде ранее не произошел выход из метода мы проверяем значение hasEmpty, если пустые ячейки есть — возвращаем 0, а если нет, то -1 (ничья).

Осталось использовать данный метод. Немного подправим обработку нажатий.

Здесь добавилось получение результата из метода checkState() и его обработка. Метод showMessageDialog() показывает сообщение с заданным текстом и заголовком. В ветвлении проверяем, какое значение вернул метод checkState() и показываем соответствующие сообщения (или продолжаем игру, если результат 0).

На этом данный туториал подходит к концу. За не столь долгое время нам удалось создать игру с графическим интерфейсом и захватывающим геймплеем: ).

Задание на дом: наша игра все-таки ленивая, и почему-то не зачеркивает выигрышный ряд. Подумайте, как можно его зачеркнуть, ведь рисовать линии вы уже умеете (подсказка, обратите внимание на метод checkState() — там уже сделана почти вся работа по нахождению выигрышного ряда).

С помощью нашего шестимесячного курса «Профессия: Разработчик» вы научитесь писать в Java не только это! 👉 Узнать подробности!

By Gaming

Learn how to program
by playing video games.

How to make a Video Game in Java (2D Basics)

February 4, 2021

This project will get you making your first game in Java! Take my starter code (I explain how it works) and build your own game! Whether your a beginner or intermediate programmer, I layout some ideas for you on what to build next to progress your skills. This is a great project for all Java students, and might even look good on that portfolio for your resume. Have fun developing your first video game!

Links

Grab this starter code on GitHub: https://github.com/learncodebygaming/java_2d_game
Need help getting setup for Java development? https://www.youtube.com/watch?v=U3UV8c8TFG4
Missed the Joy of Coding 2D Graphics project? https://www.youtube.com/watch?v=IyBsWymfqms
Recommended Java 2D Game resource: https://zetcode.com/javagames/

What’s up guys? Today I wanna show you how to make a video game in Java. If you’re in that beginner-to-intermediate range of learning how to program, and you’re looking for a project that isn’t super boring, you’re in the right place. Or even if you need a project to add to your resume: this is something that’s really visual, that can be really impressive looking.

This is an active tutorial. I’ve designed this project specifically for you to write your own code, and to make a video game that’s all your own.

We’ll be making a 2D game, using the Java Swing library, and this project builds off of the work we did in the Bob Ross «Joy of Coding» video. So if you get really confused as I start to go through this, or if you need some more practice with 2D graphics, then you should go back and do that project first. And that should give you everything you need to be ready for this one.

My idea for this project was: there’s certain things in developing a game that are just kinda difficult, but they’re unavoidable. And I don’t want to overwhelm you with things you might not be ready for yet, that maybe are acting as a barrier to you having fun with code. So I thought I’d start you off with like a template project. Just a real simple game that gets you passed some of those initial hurdles, so you can get to the fun parts.

Now I’m not going to do a full code-along this time, but I want to get you familiar with the codebase you’ll be working with. So what I think we’ll do is: in the first half of this video I want to show you how I built up this code, So that you can feel comfortable with it even if you don’t understand what every line does.

Then in the second half I want to give you a bunch of ideas and direction for different ways you can build out your game. Sort of like little homework projects that can all add up to a game that’s uniquely yours. And I’m going to be breaking down those ideas from easiest to hardest, so you can properly progress your skills as you work on your game.

So if you’re ready, the first thing you’ll want to do is: download this project off of Github, run it once to make sure it’s working, and then come back here and we’ll talk through the code. You can either clone it, if you’re familiar with Git, or you can just download the .ZIP file.

App.java

import javax.swing.*;

class App {

    private static void initWindow() {
        // create a window frame and set the title in the toolbar
        JFrame window = new JFrame("Can't Stop, Won't Stop, GameStop");
        // when we close the window, stop the app
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // create the jpanel to draw on.
        // this also initializes the game loop
        Board board = new Board();
        // add the jpanel to the window
        window.add(board);
        // pass keyboard inputs to the jpanel
        window.addKeyListener(board);
        
        // don't allow the user to resize the window
        window.setResizable(false);
        // fit the window size around the components (just our jpanel).
        // pack() should be called after setResizable() to avoid issues on some platforms
        window.pack();
        // open window in the center of the screen
        window.setLocationRelativeTo(null);
        // display the window
        window.setVisible(true);
    }

    public static void main(String[] args) {
        // invokeLater() is used here to prevent our graphics processing from
        // blocking the GUI. https://stackoverflow.com/a/22534931/4655368
        // this is a lot of boilerplate code that you shouldn't be too concerned about.
        // just know that when main runs it will call initWindow() once.
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                initWindow();
            }
        });
    }
}

Board.java

import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.*;

public class Board extends JPanel implements ActionListener, KeyListener {

    // controls the delay between each tick in ms
    private final int DELAY = 25;
    // controls the size of the board
    public static final int TILE_SIZE = 50;
    public static final int ROWS = 12;
    public static final int COLUMNS = 18;
    // controls how many coins appear on the board
    public static final int NUM_COINS = 5;
    // suppress serialization warning
    private static final long serialVersionUID = 490905409104883233L;
    
    // keep a reference to the timer object that triggers actionPerformed() in
    // case we need access to it in another method
    private Timer timer;
    // objects that appear on the game board
    private Player player;
    private ArrayList coins;

    public Board() {
        // set the game board size
        setPreferredSize(new Dimension(TILE_SIZE * COLUMNS, TILE_SIZE * ROWS));
        // set the game board background color
        setBackground(new Color(232, 232, 232));

        // initialize the game state
        player = new Player();
        coins = populateCoins();

        // this timer will call the actionPerformed() method every DELAY ms
        timer = new Timer(DELAY, this);
        timer.start();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        // this method is called by the timer every DELAY ms.
        // use this space to update the state of your game or animation
        // before the graphics are redrawn.

        // prevent the player from disappearing off the board
        player.tick();

        // give the player points for collecting coins
        collectCoins();

        // calling repaint() will trigger paintComponent() to run again,
        // which will refresh/redraw the graphics.
        repaint();
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        // when calling g.drawImage() we can use "this" for the ImageObserver 
        // because Component implements the ImageObserver interface, and JPanel 
        // extends from Component. So "this" Board instance, as a Component, can 
        // react to imageUpdate() events triggered by g.drawImage()

        // draw our graphics.
        drawBackground(g);
        drawScore(g);
        for (Coin coin : coins) {
            coin.draw(g, this);
        }
        player.draw(g, this);

        // this smooths out animations on some systems
        Toolkit.getDefaultToolkit().sync();
    }

    @Override
    public void keyTyped(KeyEvent e) {
        // this is not used but must be defined as part of the KeyListener interface
    }

    @Override
    public void keyPressed(KeyEvent e) {
        // react to key down events
        player.keyPressed(e);
    }

    @Override
    public void keyReleased(KeyEvent e) {
        // react to key up events
    }

    private void drawBackground(Graphics g) {
        // draw a checkered background
        g.setColor(new Color(214, 214, 214));
        for (int row = 0; row < ROWS; row++) {
            for (int col = 0; col < COLUMNS; col++) {
                // only color every other tile
                if ((row + col) % 2 == 1) {
                    // draw a square tile at the current row/column position
                    g.fillRect(
                        col * TILE_SIZE, 
                        row * TILE_SIZE, 
                        TILE_SIZE, 
                        TILE_SIZE
                    );
                }
            }    
        }
    }

    private void drawScore(Graphics g) {
        // set the text to be displayed
        String text = "$" + player.getScore();
        // we need to cast the Graphics to Graphics2D to draw nicer text
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(
            RenderingHints.KEY_TEXT_ANTIALIASING,
            RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g2d.setRenderingHint(
            RenderingHints.KEY_RENDERING,
            RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(
            RenderingHints.KEY_FRACTIONALMETRICS,
            RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        // set the text color and font
        g2d.setColor(new Color(30, 201, 139));
        g2d.setFont(new Font("Lato", Font.BOLD, 25));
        // draw the score in the bottom center of the screen
        // https://stackoverflow.com/a/27740330/4655368
        FontMetrics metrics = g2d.getFontMetrics(g2d.getFont());
        // the text will be contained within this rectangle.
        // here I've sized it to be the entire bottom row of board tiles
        Rectangle rect = new Rectangle(0, TILE_SIZE * (ROWS - 1), TILE_SIZE * COLUMNS, TILE_SIZE);
        // determine the x coordinate for the text
        int x = rect.x + (rect.width - metrics.stringWidth(text)) / 2;
        // determine the y coordinate for the text
        // (note we add the ascent, as in java 2d 0 is top of the screen)
        int y = rect.y + ((rect.height - metrics.getHeight()) / 2) + metrics.getAscent();
        // draw the string
        g2d.drawString(text, x, y);
    }

    private ArrayList populateCoins() {
        ArrayList coinList = new ArrayList<>();
        Random rand = new Random();

        // create the given number of coins in random positions on the board.
        // note that there is not check here to prevent two coins from occupying the same
        // spot, nor to prevent coins from spawning in the same spot as the player
        for (int i = 0; i < NUM_COINS; i++) {
            int coinX = rand.nextInt(COLUMNS);
            int coinY = rand.nextInt(ROWS);
            coinList.add(new Coin(coinX, coinY));
        }

        return coinList;
    }

    private void collectCoins() {
        // allow player to pickup coins
        ArrayList collectedCoins = new ArrayList<>();
        for (Coin coin : coins) {
            // if the player is on the same tile as a coin, collect it
            if (player.getPos().equals(coin.getPos())) {
                // give the player some points for picking this up
                player.addScore(100);
                collectedCoins.add(coin);
            }
        }
        // remove collected coins from the board
        coins.removeAll(collectedCoins);
    }

}

Player.java

import java.awt.event.KeyEvent;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.awt.Point;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

public class Player {

    // image that represents the player's position on the board
    private BufferedImage image;
    // current position of the player on the board grid
    private Point pos;
    // keep track of the player's score
    private int score;

    public Player() {
        // load the assets
        loadImage();

        // initialize the state
        pos = new Point(0, 0);
        score = 0;
    }

    private void loadImage() {
        try {
            // you can use just the filename if the image file is in your
            // project folder, otherwise you need to provide the file path.
            image = ImageIO.read(new File("images/player.png"));
        } catch (IOException exc) {
            System.out.println("Error opening image file: " + exc.getMessage());
        }
    }

    public void draw(Graphics g, ImageObserver observer) {
        // with the Point class, note that pos.getX() returns a double, but 
        // pos.x reliably returns an int. https://stackoverflow.com/a/30220114/4655368
        // this is also where we translate board grid position into a canvas pixel
        // position by multiplying by the tile size.
        g.drawImage(
            image, 
            pos.x * Board.TILE_SIZE, 
            pos.y * Board.TILE_SIZE, 
            observer
        );
    }
    
    public void keyPressed(KeyEvent e) {
        // every keyboard get has a certain code. get the value of that code from the
        // keyboard event so that we can compare it to KeyEvent constants
        int key = e.getKeyCode();
        
        // depending on which arrow key was pressed, we're going to move the player by
        // one whole tile for this input
        if (key == KeyEvent.VK_UP) {
            pos.translate(0, -1);
        }
        if (key == KeyEvent.VK_RIGHT) {
            pos.translate(1, 0);
        }
        if (key == KeyEvent.VK_DOWN) {
            pos.translate(0, 1);
        }
        if (key == KeyEvent.VK_LEFT) {
            pos.translate(-1, 0);
        }
    }

    public void tick() {
        // this gets called once every tick, before the repainting process happens.
        // so we can do anything needed in here to update the state of the player.

        // prevent the player from moving off the edge of the board sideways
        if (pos.x < 0) {
            pos.x = 0;
        } else if (pos.x >= Board.COLUMNS) {
            pos.x = Board.COLUMNS - 1;
        }
        // prevent the player from moving off the edge of the board vertically
        if (pos.y < 0) {
            pos.y = 0;
        } else if (pos.y >= Board.ROWS) {
            pos.y = Board.ROWS - 1;
        }
    }

    public String getScore() {
        return String.valueOf(score);
    }

    public void addScore(int amount) {
        score += amount;
    }

    public Point getPos() {
        return pos;
    }

}

Coin.java

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.awt.Point;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

public class Coin {
    
    // image that represents the coin's position on the board
    private BufferedImage image;
    // current position of the coin on the board grid
    private Point pos;

    public Coin(int x, int y) {
        // load the assets
        loadImage();

        // initialize the state
        pos = new Point(x, y);
    }

    private void loadImage() {
        try {
            // you can use just the filename if the image file is in your
            // project folder, otherwise you need to provide the file path.
            image = ImageIO.read(new File("images/coin.png"));
        } catch (IOException exc) {
            System.out.println("Error opening image file: " + exc.getMessage());
        }
    }

    public void draw(Graphics g, ImageObserver observer) {
        // with the Point class, note that pos.getX() returns a double, but 
        // pos.x reliably returns an int. https://stackoverflow.com/a/30220114/4655368
        // this is also where we translate board grid position into a canvas pixel
        // position by multiplying by the tile size.
        g.drawImage(
            image, 
            pos.x * Board.TILE_SIZE, 
            pos.y * Board.TILE_SIZE, 
            observer
        );
    }

    public Point getPos() {
        return pos;
    }

}

Now let’s talk about some of the things you can do with this starter code, to build out your own game. You’re not meant to do all of these. Just pick and choose things you like or want to try.
And if you have any ideas that aren’t on these lists, definitely just go for it.

Beginner

  • Change the colors that are being used.
  • Change the image files that are being used.
  • Change how many points you get per coin.
  • Make instance or class variables to control hardcoded values like the ones just mentioned.
  • Use WASD instead of arrows for movement.
  • Change the dimensions of the game board.
  • Make a new coin appear whenever the player picks one up.
  • Change the tile size.
    • Remember to update your image files, or scale the images.

Intermediate

  • Make coins disappear after some time.
    • By ticks, or using a separate timer, or after the player has moved so many squares.
  • Make more coins appear at random intervals.
  • Replace the checkered background with an image.
  • Player and Coin share a lot of commonalities. Create a parent class that both of these classes extend from to reduce code duplication.
  • Make a special coin that looks different and is worth more points.
  • End or restart the game when all coins are collected, or when a certain score is reached.
  • Decide what winning means, then redraw the whole canvas with a celebration graphic when you win the game.
  • Add a game clock.
    • Could count up or down.
    • Could replace the score or be in addition to the score.
    • Display it like the score.
  • Keep track of high scores.
    • In a single play session.
    • Or across all sessions by reading/writing to a file.
  • Allow the player to wrap around the edges of the board.

Advanced

  • Add obstacles to block player movement.
  • Add an object type that reduces your score when touched. Or maybe it ends the game.
  • Make an object type that moves around on its own. Could be like the ghosts from pacman.
  • Add sounds.
    • When a player moves, or picks up coins.
    • A constant sound track.
    • A sound that plays when you first open the game.
  • Implement delta movements to only allow players to move one tile per tick.
    • Can play around with the tick rate when developing this.
    • React to both pressed and released.
    • Can enable diagonal movements.
    • Fixes the issue caused by holding down a key, and makes for a more responsive experience.
  • Make the total game area larger than the portion of that grid we see in the game window.
    • So maybe the viewport moves as the player approaches an edge.
    • Or maybe the player stays in the middle and the whole viewport moves with the player whenever the player moves.
  • Think about other keyboard keys that might perform some other action.
    • Can be as simple as changing some colors in the game.
    • Or maybe you’ve got health potions to restore player health.
  • Add more scoreboard/HUD elements.
    • Maybe move this off of the game board itself, to some designated area on the canvas. I recommend still using just the single JPanel if you want to do this.
  • Eliminate the grid concept altogether and use pixel positions instead.

Expert

To dive even deeper into developing your Java 2D game, I recommend checking out this series of tutorials for guidance and inspiration: https://zetcode.com/javagames/


Alright so good luck on building what’s probably your first computer game! Hopefully you’ve got a lot of ideas, and some sense of how you might accomplish them. I really tried to strip this project down, and make it as easy for you to build off of as possible.

Once you’ve finished your project, and you’re wondering what’s next? First of all I’m super proud of you: you’ve accomplished something most people will only ever dream of. And the next step would be to just make another game, but push yourself to be a little more ambitious this second time around.

And then after that you’re ready to graduate to a proper game development library. I’ve never used any Java game development libraries, so I can’t really give you any recommendations there. But let me know if you end up finding one you like, and maybe I can check that out in a future video.


Ben Johnson
My name is Ben and I help people learn how to code by gaming.
I believe in the power of project-based learning to foster a deep understanding and joy in the craft of
software development.
On this site I share programming tutorials, coding-game reviews, and project ideas for you to explore.

Подробные руководства, как создать простые игры на Java меньше, чем за час

Как написать свой Тетрис на Java за полчаса

В предыдущих статьях этой серии мы уже успели написать сапёра, змейку и десктопный клон игры 2048. П…

Обложка: Как написать свой Тетрис на Java за полчаса

Как написать свою 2048 на Java за 15 минут

В предыдущих статьях этой серии мы уже писали сапёра и змейку на Java, а теперь попробуем написать десктопный клон игры 2048.

Обложка: Как написать свою 2048 на Java за 15 минут

Как написать свою змейку на Java за 15 минут

В предыдущей статье мы писали сапёра за 15 минут, теперь займёмся классической змейкой.В предыдущей …

Как написать своего сапёра на Java за 15 минут

Нам понадобятся: 15 минут свободного времени; Настроенная рабочая среда, т.е. JDK и IDE (например Eclips…

Увы, но нам не удалось найти больше постов.

Привет хаброжители. Данный пост является «рерайтом» моего поста для песочницы. На этот раз я постараюсь охватить больше тем, чем тогда.

Почему Java?

Ничего объективного я тут не скажу, а скажу лишь то, что я люблю этот язык, и мне нравиться писать на нем. Да, на Java нет игр AAA-класса, но Java предоставляет огромные возможности, больше кол-во встроенных средств и быстроту написания кода.

Начнем с выбора IDE. Я являюсь фанатом Eclipse и посоветую вам его.
Если же почему-то вам он не понравился, вы можете использовать NetBeans, Intellij IDEA или командную строку и ваш любимый редактор.

И скачаем JDK последней версии: JDK 7u4

Скорее всего проблем с установкой IDE у вас не возникнет, а если у вас 64-битная система, все же посоветую устанавливать 32-битный Eclipse, так как иногда бывают ошибки и Eclipse у вас просто не запустится.

Под катом мы приступим к созданию игры.

Класс Game

Итак, создаем проект, в нем класс Game(попутно создав в нем точку входа). Данный класс должен наследовать класс Canvas и реализовать интерфейс Runnable:

Создадим переменную running типа Boolean, которая, как вы уже догадались будет показывать нам запущена ли игра, или нет.

Создадим функцию start() и в ней мы будем создавать новый поток и переводить running в true:

Создадим три функции — update(long delta), render() и init(). Я надеюсь что их значение вам понятно. В функции run() создадим главный игровой цикл, перед ним будем вызывать init(), а в нем самом render() и update(). Так же мы будем вычислять разницу между кадрами(delta time).

Пока поработаем над функцией render().

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

Примерно вот так выглядит наш класс Game сейчас.

Класс Sprite

Создадим новый класс Sprite. Поскольку этот класс небольшой, я сразу приведу весь его код с комментариями:

Сразу же проверим работоспособность. Возьмем эту картинку и скопируем ее в папку с нашим классом Sprite. Добавим функцию getSprite() в класс Game(временно).

Добавим нашу картинку в папку assets(папку создать в корне проекта), саму папку надо добавить в build path.

Далее создаем переменную hero типа Sprite. В функции init() инициализируем ее. В Функции render() рисуем:

Input

Для обработки инпута мы создадим класс, наследующий KeyAdapter:

Тут же и объявим две переменных в шапке класса Game:

Внутри класса KeyInputHandler создадим две функции:

Теперь в функции init() добавим следующее:

Создадим переменные x и y для героя(так как пока что мы еще не написали класс Entity). Сделаем чтобы герой всегда рисовался на этих координатах.

А теперь в функции update() будем проверять нажаты ли клавиши и изменять x-координату.

Создание простых игр на Java

Как написать свой Тетрис на Java за полчаса

В предыдущих статьях этой серии мы уже успели написать сапёра, змейку и десктопный клон игры 2048. П.

  • 0
  • 0
  • 0
  • 0
  • 0

Как написать свою 2048 на Java за 15 минут

В предыдущих статьях этой серии мы уже писали сапёра и змейку на Java, а теперь попробуем написать десктопный клон игры 2048.

  • 0
  • 0
  • 0
  • 0
  • 0

Как написать свою змейку на Java за 15 минут

В предыдущей статье мы писали сапёра за 15 минут, теперь займёмся классической змейкой.В предыдущей .

Раздел «Игры» на JavaRush: Игровой движок

Раздел

void setCellColor(int x, int y, Color color) — устанавливает клетке с координатами (x, y) цвет color:

Color getCellColor(int x, int y) — возвращает цвет клетки с координатами (x, y):

void setCellValue(int x, int y, String value) — помещает в клетку с координатами (x, y) текст value:

String getCellValue(int x, int y) — возвращает текст, содержащийся в клетке с координатами (x, y):

void setCellTextSize(int x, int y, int size) — устанавливает размер контента в клетке с координатами (x, y). size – высота текста в процентах от высоты клетки:

int getCellTextSize(int x, int y) — возвращает размер контента в клетке с координатами (x, y):

void setCellNumber(int x, int y, int value) — помещает в клетку с координатами (x, y) число value:

int getCellNumber(int x, int y) — возвращает число, содержащееся в клетке с координатами (x, y). Если в клетке содержится не число, возвращает 0:

void setCellTextColor(int x, int y, Color color) — устанавливает цвет контента(текста) в клетке с координатами (x, y):

Color getCellTextColor(int x, int y) — возвращает цвет контента (текста) в клетке с координатами (x, y):

void setCellValueEx(int x, int y, Color cellColor, String value) — устанавливает клетке с координатами (x, y) цвет фона cellColor и контент value:

void setCellValueEx(int x, int y, Color cellColor, String value, Color textColor) — устанавливает клетке с координатами (x, y) цвет фона cellColor, контент value и цвет контента textColor:

void setCellValueEx(int x, int y, Color cellColor, String value, Color textColor, int textSize); — устанавливает клетке с координатами (x, y) цвет фона cellColor, контент value, цвет контента textColor и размер контента textSize:

Время на прочтение
4 мин

Количество просмотров 266K

Введение и подготовка

Привет хаброжители. Данный пост является «рерайтом» моего поста для песочницы. На этот раз я постараюсь охватить больше тем, чем тогда.

Почему Java?

Ничего объективного я тут не скажу, а скажу лишь то, что я люблю этот язык, и мне нравиться писать на нем. Да, на Java нет игр AAA-класса, но Java предоставляет огромные возможности, больше кол-во встроенных средств и быстроту написания кода.

IDE

Начнем с выбора IDE. Я являюсь фанатом Eclipse и посоветую вам его.
Если же почему-то вам он не понравился, вы можете использовать NetBeans, Intellij IDEA или командную строку и ваш любимый редактор.

JDK

И скачаем JDK последней версии: JDK 7u4

Скорее всего проблем с установкой IDE у вас не возникнет, а если у вас 64-битная система, все же посоветую устанавливать 32-битный Eclipse, так как иногда бывают ошибки и Eclipse у вас просто не запустится.

Под катом мы приступим к созданию игры.

Класс Game

Итак, создаем проект, в нем класс Game(попутно создав в нем точку входа). Данный класс должен наследовать класс Canvas и реализовать интерфейс Runnable:

public class Game extends Canvas implements Runnable {
	private static final long serialVersionUID = 1L;

	public void run() { //функция run появляется после того, как мы добавили "implements Runnable"
	}

	public static void main(String[] args) {
	}
}

Создадим переменную running типа Boolean, которая, как вы уже догадались будет показывать нам запущена ли игра, или нет.

Создадим функцию start() и в ней мы будем создавать новый поток и переводить running в true:

public void start() {
	running = true;
	new Thread(this).start();
}

Создадим три функции — update(long delta), render() и init(). Я надеюсь что их значение вам понятно. В функции run() создадим главный игровой цикл, перед ним будем вызывать init(), а в нем самом render() и update(). Так же мы будем вычислять разницу между кадрами(delta time).

public void run() {
	long lastTime = System.currentTimeMillis();
	long delta;
	
	init();
		
	while(running) {
		delta = System.currentTimeMillis() - lastTime;
		lastTime = System.currentTimeMillis();	
		update(delta);
		render();
	}
}
	
public void init() {
		
}
	
public void render() {

}
	
public void update(long delta) {
		
}

Пока поработаем над функцией render().

public void render() {
	BufferStrategy bs = getBufferStrategy(); 
	if (bs == null) {
		createBufferStrategy(2); //создаем BufferStrategy для нашего холста
		requestFocus();
		return;
	}
		
	Graphics g = bs.getDrawGraphics(); //получаем Graphics из созданной нами BufferStrategy
	g.setColor(Color.black); //выбрать цвет
	g.fillRect(0, 0, getWidth(), getHeight()); //заполнить прямоугольник 
	g.dispose();
	bs.show(); //показать
}

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

public static int WIDTH = 400; //ширина
public static int HEIGHT = 300; //высота
public static String NAME = "TUTORIAL 1"; //заголовок окна

public static void main(String[] args) {
	Game game = new Game();
	game.setPreferredSize(new Dimension(WIDTH, HEIGHT));

	JFrame frame = new JFrame(Game.NAME);
	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //выход из приложения по нажатию клавиши ESC
	frame.setLayout(new BorderLayout());
	frame.add(game, BorderLayout.CENTER); //добавляем холст на наш фрейм
	frame.pack();
	frame.setResizable(false);
	frame.setVisible(true);

	game.start();
}

Примерно вот так выглядит наш класс Game сейчас.

Класс Sprite

Создадим новый класс Sprite. Поскольку этот класс небольшой, я сразу приведу весь его код с комментариями:

import java.awt.Graphics;
import java.awt.Image;

public class Sprite {
	private Image image; //изображение
	
	public Sprite(Image image) {
		this.image = image;
	}
	
	public int getWidth() { //получаем ширину картинки
		return image.getWidth(null);
	}

	public int getHeight() { //получаем высоту картинки
		return image.getHeight(null);
	}
	
	public void draw(Graphics g,int x,int y) { //рисуем картинку
		g.drawImage(image,x,y,null);
	}
}

Сразу же проверим работоспособность. Возьмем эту картинку и скопируем ее в папку с нашим классом Sprite. Добавим функцию getSprite() в класс Game(временно).

public Sprite getSprite(String path) {
	BufferedImage sourceImage = null;
		
	try {
		URL url = this.getClass().getClassLoader().getResource(path);
		sourceImage = ImageIO.read(url);
	} catch (IOException e) {
		e.printStackTrace();
	}

	Sprite sprite = new Sprite(Toolkit.getDefaultToolkit().createImage(sourceImage.getSource()));
		
	return sprite;
}

Добавим нашу картинку в папку assets(папку создать в корне проекта), саму папку надо добавить в build path.

Далее создаем переменную hero типа Sprite. В функции init() инициализируем ее. В Функции render() рисуем:

//в "шапку"
public static Sprite hero;

//в init()
hero = getSprite("man.png");

//в render() после g.fillRect(0, 0, getWidth(), getHeight());
hero.draw(g, 20, 20);

Результат:

Весь код Game.java

Input

Для обработки инпута мы создадим класс, наследующий KeyAdapter:

private class KeyInputHandler extends KeyAdapter {

}

Тут же и объявим две переменных в шапке класса Game:

private boolean leftPressed = false;
private boolean rightPressed = false; 

Внутри класса KeyInputHandler создадим две функции:

public void keyPressed(KeyEvent e) { //клавиша нажата
	if (e.getKeyCode() == KeyEvent.VK_LEFT) {
		leftPressed = true;
	}
	if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
		rightPressed = true;
	}	
} 	
public void keyReleased(KeyEvent e) { //клавиша отпущена
	if (e.getKeyCode() == KeyEvent.VK_LEFT) {
		leftPressed = false;
	}
	if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
		rightPressed = false;
	}
}

Теперь в функции init() добавим следующее:

addKeyListener(new KeyInputHandler());

Создадим переменные x и y для героя(так как пока что мы еще не написали класс Entity). Сделаем чтобы герой всегда рисовался на этих координатах.

private static int x = 0;
private static int y = 0;

hero.draw(g, x, y);

А теперь в функции update() будем проверять нажаты ли клавиши и изменять x-координату.

public void update(long delta) {
	if (leftPressed == true) {
		x--;
	}
	if (rightPressed == true) {
		x++;
	}
}

Он двигается!

Спасибо за внимание.

P.S. Ссылка на github

Создание развлекательных приложений и игр – перспективные направления в программировании. Пользователи готовы не только скачивать соответствующий контент бесплатно, но и платить за него. Особенно тогда, когда софт получается действительно качественным.

Во время создания игр можно использовать разнообразные языки программирования. Некоторые разработчики предпочитают Си-семейство. Оно универсально, но новичкам «с нуля» приступить к коддингу будет трудно. Поэтому тем, кто только начинает изучать процесс разработки игр и программирования, стоит обратить внимание на Java. Это – весьма простой язык, посредством которого можно создавать уникальные перспективные проекты с минимальными трудностями. Основной принцип Джавы – меньше писать, больше делать.

Движок – это…

Перед тем, как приступать к работе с кодом и задумываться над созданием игр, нужно уяснить – справиться с поставленной задачей удастся только при помощи так называемого движка. Термин слышали многие, но не все понимают, что это такое.

Игровой движок – совокупность неких модулей программного типа, которые включают в себя различные элементы, задействованных при создании игр и развлекательных приложений. Готовый «сборник» утилит. Выступает в качестве базового программного обеспечения при разработке игрового софта.

При помощи движка можно обеспечить:

  • музыкальное и звуковое сопровождение;
  • сетевую игру;
  • графическую визуализацию;
  • перемещение внутриигровых персонажей (а также их взаимодействие согласно имеющимся скриптам);
  • соблюдение физических эффектов и закономерностей;
  • встроенные графические сцены.

Грамотно подобранный движок дает разработчику больше возможностей при коддинге. С ним создавать игры для Андроид, Windows/Mac и iOS просто и интересно. Навыки программирования могут быть минимальными.

Движки для программистов и библиотеки на Джаве

Ява – язык программирования, который пользуется у программистов очень большим спросом. Освоить его способен даже новичок без существенных затруднений. Большинство современных платформ для создания игр поддерживают Java-семейство. Это позволяет программерам и разрабам выбрать оптимальный для себя «пакет» готовых утилит при создания развлекательного контента. Далее будут перечислены лучшие движки JavaScript и библиотеки.

GDevelop

Универсальная утилита – подходит и новичкам, и продвинутым программистам. С ее помощью можно сделать:

  • платформеры;
  • шутеры;
  • элементарные игры 8-bit.

Создана для того, чтобы дать возможность разработчикам (особенно новичкам) освоить работу с 2d-играми. Трехмерную графику не поддерживает.

GDevelop предлагает экспорт на различные платформы: Android, iOS, FaceBook (ныне Meta) Instant Games и не только. Подойдет тем, кто заинтересован в экспортировании игр, но не хочет углубляться в непосредственную разработку софта и долго изучать низкоуровневую архитектуру игровых движков.

MelonJS

Еще один вариант, если хотите научиться делать собственные 2D-игры. Подключив соответствующую библиотеку к коду, можно получить доступ к качественной поддержке:

  • физики;
  • столкновений;
  • спрайтов;
  • деформаций.

В успешных проектах все это играет огромную роль. Из минусов – не самая лучшая документация. Зато пользовательского контента у MelonJS полно. А еще имеется отличное комьюнити.

Идеальный вариант для новичков. Является весьма мощным инструментом в умелых руках. Но продвинутым программерам предлагаемых возможностей будет маловато.

ImpactJS

Имеет ориентацию преимущественно на двухмерную графику. В отличие от предыдущих вариантов обладает плагинами, которые при добавлении в Impact позволяют имитировать 3D-среду.

Дополнительно к Impact «идут» следующие инструменты:

  • редактор уровней;
  • дебаггер;
  • фреймворк для публикации в Ejecta.

Через Impact удается без проблем размещать утилиты в AppStore.

Babylon

Мощный инструмент, предусматривающий веб-рендеринг. Игровым движком его назвать нельзя, но на основе BabylonJS удастся создать game. Движок рендеринга предусматривает доступ к низкоуровневому функционалу.

Babylon – утилита, которая подойдет продвинутым программистам. Новичкам с ней работать затруднительно, особенно если учесть, что это – не совсем игровой движок.

PhaserJS

Среди популярных вариантов, поддерживающих Java, выделяют PhaserJS. Он позволяет программировать не только для компьютеров, но и для мобильных устройств. Обладает поддержкой WebGL. Годится для написания 2D-софта.

Это – бесплатный движок. За дополнительную плату можно подключить особые плагины, значительно увеличивающие мощь «пакета».

Pixi

Библиотека, задействованная при программировании в двухмерном пространстве. Работает с WebGL, задействуется для воплощения потрясающих интерфейсов. И не обязательно они будут размещаться в играх.

Включает в себя:

  • шейдеры;
  • текст;
  • спрайты.

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

PlayCanvas

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

PlayCanvas – условно-бесплатный «набор программиста». Годится для небольших публичных проектов. За «тайные» коммерческие идеи предстоит платить ежемесячно.

A-Frame

Инновационное решение в программировании. A-Frame может предоставить пользователям больше возможностей, нежели предыдущие библиотеки. И все это за счет того, что ориентирован движок на VR и AR.

Синтаксис напоминает HTML-верстку. Подойдет для 3d-программирования с «полным погружением». В основном утилитой пользуются опытные программеры.

PhysicsJS

Основан на физическом взаимодействии имеющихся объектов. Используется при разработке всех видов игрушек. Для Андроид в том числе.

Отличное решение для тех, кому важна сложная физика в реализуемом софте. То же самое касается применения при создании собственных движков на основе уже имеющихся библиотек.

Универсальные решения для программистов

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

Unreal Engine 4

Настоящая легенда в сфере gaming programming. Разрабатывался «пакет» с 1998 года. С тех пор все время совершенствуется и дорабатывается. Современная версия UE 4 является универсальной. При помощи нее создаются развлекательные приложения для:

  • игровых консолей;
  • мобильных платформ;
  • компьютеров.

Является частично бесплатным. Платить за использование оного не нужно, если прибыль с созданного приложения в месяц не переваливает за 3 000 долларов США. В противном случае предстоит переводить создателям движка проценты с получаемых доходов.

Unity

Юнити – популярный вариант среди разработчиков. Обошел иные платформы для создания игр, благодаря простоте осваивания. Развивается с 2005 года.

Подойдет для 3D-игрушек. Как и предыдущий вариант, является кроссплатформенным. На Юнити пишут не только простые игры (головоломки, аркады), но и шутеры от первого лица с тщательно проработанным игровым миром.

Недостаток один – графика в созданных утилитах далека от 100% реалистичности. Если разработчику важна графическая составляющая, лучше пользоваться UE 4. Несмотря на это, более половины утилит для Android написаны именно на Unity. Подходит как новичкам, так и продвинутым программистам.

Corona

Программы для создания игрушек можно перечислять бесконечно долго. И выбрать что-то одно бывает непросто. Добавить к списку наиболее успешных и популярных «пакетов» можно утилиту под названием Corona SDK.

Он выступает в качестве платформы для двухмерных игр. Предусматривает:

  • поддержку API;
  • сложные функции в 2D-играх;
  • в основе API используется Luna;
  • монетизацию через Corona Ads.

Данный вариант является кроссплатформенным. Подходит и для Андроид, и для iOS. Осваивается без существенных затруднений, поэтому идеальна для новичков. Имеется тестирование в режиме реального времени.

Обладает разнообразными полезными фитчами:

  • Sublime Text;
  • Corona Editor;
  • Composer GUI.

Через Corona’s Physycs Engine можно отслеживать взаимодействие игровых объектов между собой. Этот прием позволяет довести физику в развлекательном софте до идеального состояния с минимальными временными потерями.

Как создать собственную игру – советы

Для того, чтобы писать программы и игровой контент на Java, рекомендуется изучить основы программирования. Существуют следующие варианты развития событий:

  1. Самообразование. Результат виден лишь у целеустремленных будущих программистов. В интернете полно полезной информации по рассмотренной тематике. И не всегда за нее нужно платить.
  2. Обучение в ВУЗе. Подойдет направление «Программирование». В некоторых университетах есть вариант «геймдев». Долгий и дорогостоящий вариант, но в результате на руках окажется диплом. Выпускник сможет писать сложные программы.
  3. Прохождение курсов.

Последний вариант больше всего подходит тем, кто не готов сидеть 5 и более лет в университете. Специализированные образовательные центры предлагают как очные/заочные курсы, так и дистанционные.

Преимуществом такого подхода является то, что человек может выбрать узкую направленность. Пример – изучение только процесса создания игр на Android или iOS. В конце обучения (длится до года) выдается сертификат установленного образца. При желании можно изучать движки для игр iOS, Windows, Android более подробно. Для самых популярных «утилит» существуют отдельные курсы. Делятся они по уровню навыков. Подходящие уроки подберет себе и новичок, и продвинутый программер.

Понравилась статья? Поделить с друзьями:
  • Как написать свою графическую библиотеку
  • Как написать свою былину для 6 класса
  • Как написать свою браузерную игру
  • Как написать свою биографию план
  • Как написать свою биографию образец на работу для женщины