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

В этой статье будет описываться создание 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
Поделиться с друзьями
-->

Комментарии (27)


  1. AndreyRubankov
    09.04.2017 22:45
    +9

    Похвально, что вы изучаете технологию и пишите статью.
    Но под заголовком 2d игра на Java ожидал увидеть еще одну разработку под Android, с одним из десятков интересных игровых движков. Или хотя бы на базе JOGL что-то. Но никак не swing + awt.
    Все же swing — это не тот фреймворк, на котором стоило бы делать игры.


    1. izzholtik
      10.04.2017 11:03

      Так свинг же тут только для отрисовки окна. Надеюсь.


      1. Googolplex
        10.04.2017 12:23

        А для рисования используется AWT, да. Что-то производительное и сложное на этом написать трудно, Graphics и Graphics2D в первую очередь сделаны для рисования кастомных виджетов, не для сложных двигающихся изображений.


    1. AtStudio
      12.04.2017 11:03

      Статью про игру под Android я не хотел писать, т.к. есть уже более хорошие примеры.


  1. PapaBubaDiop
    09.04.2017 22:51
    +9

    2D игра с видом от 3-его лица. Не могу преодолеть эту фразу. Подозреваю, что и Java несколько не Java…


    1. AtStudio
      12.04.2017 10:55

      Имелось ввиду, 2d игра с видом сверху


      1. PapaBubaDiop
        12.04.2017 11:52

        Судя по картинкам, скорее сверху-сбоку


  1. Rogvold91
    10.04.2017 01:09
    -4

    Если захотите писать игры на Java — зайтесь вопросом, кто будет в неё играть? Если хотите написать игру чуть более чем для себя (да и со стороны изучения самой Java) полезно будет подумать про программирование игр для андроида. Соответственно:

    1. OpenGl Es. OpenGL для андроида. Есть Java, а есть и С++. Выбирайте, что по душе и вперёд.
    2. Производительность. На мой скромный взгляд Java врядли подойдёт. Попробуйте связку Java и C++ через JNI.



  1. GeMir
    10.04.2017 01:13

    «Теперь разберемся, что мы сделали.» — Любопытный подход: сначала делаем что-то, а потом пытаемся понять, что же мы сделали. Напоминает последовательность «теорема и следом доказательство».


    1. AtStudio
      12.04.2017 11:11

      Учту ошибки


  1. Sirion
    10.04.2017 01:13
    +11

    Простите, но почему домик не набигает?


    1. Rogvold91
      10.04.2017 01:21
      +3

      Злодея нет и стражи


  1. ls18
    10.04.2017 07:14
    +2

    А корованы грабить можно будет?


    1. AtStudio
      12.04.2017 10:52

      Все может быть


  1. vlanko
    10.04.2017 09:10

    Как я начинал создавать 3Д движок на Яве…


  1. anderston
    10.04.2017 12:53

    Посмотрите еще в сторону Lightweight Java Game Library.


  1. barker
    10.04.2017 16:55

    На сегодня все. Оставляю код, для тех, кто запутался:
    Вах! Да это же игра в 30 строк на Java!
    До второй главы мануала по swing-у дочитаете — обязательно пишите продолжение.


  1. BIanF
    11.04.2017 11:02

    Одного меня смутил таймер для отрисовки?


    1. barker
      11.04.2017 13:36
      +1

      Это всё что вас смутило в этой заметке, даже больше чем 17 плюсов статье?


  1. kernelconf
    12.04.2017 10:48

    А продолжение будет? Мне было интересно (всегда писал консольные приложения на Яве в прикладных целях)


    1. AtStudio
      12.04.2017 10:49

      Конечно будет, если хабр одобрит )


  1. Wannaasbird
    12.04.2017 10:48

    Я думаю тут статья для тех кто вообще темного понятия о создании игр. Типа меня ) Мне понравилось. Написано проще некуда.


  1. darkslave
    12.04.2017 10:49

    без обид, но я не пойму, в чем смысл статей об устаревших технологиях… на мой взгляд такие статьи не только бессмысленны, но и вредны… наивный дурачок зайдет на хабр, увидит статью по технологиям 15-летней давности, подумает: «Ага, раз пишут на хабре, то это норма!» — и станет использовать это у себя в коде… :-)


    1. AtStudio
      12.04.2017 10:50

      Честно, я считаю, что надо начинать с простого, понять основы, прежде чем браться за что-то более сложное.


      1. AndreyRubankov
        12.04.2017 16:40

        Это хороший подход, вот только Swing и AWT — это далеко не основы, а довольно большие фреймворки для разработки десктопного ПО.

        По моим скромным ощущениям, работа со swing пропитана болью. Чтобы создать обработчик события — нужно либо создавать отдельный класс, либо городить огромнейшие конструкции с анонимными классами, которые дергают текущее состояние основного класса. Тяжелые вычисления нужно выносить из UI потока, и играться с перекидыванием данных между потоками.

        Можете взять за основу PlayN фреймворк. Он больше подходит для вашей задачи. Он довольно простой и позволяет создавать десктопные, мобильные и веб приложения. И статей про этот фреймворк на Хабре не сильно много и в геймдеве он больше востребован, чем swing и awt (для поиска работы в геймдеве лучше).


        1. AtStudio
          12.04.2017 18:27

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