Перейти к содержимому

Как сделать 3d игру на java

  • автор:

3D графика на Java. Какую программу/движок использовать?

Я уже довольно давно создаю программы (по большей степени игры) на Java. Раньше создавал только игры с 2D графикой и хочется попробовать себя в 3D, но не могу найти хорошего движка/обучения для создания 3D графики на Java. Что порекомендуете? Если сможете что-то порекомендовать, то просьба также оставить ссылку на сайт, где можно ему научиться, и желательно уточнить, насколько долго его необходимо изучать, чтобы создать первые 3D игры. Заранее спасибо.

  • Вопрос задан более трёх лет назад
  • 1693 просмотра

Комментировать
Решения вопроса 1

azerphoenix

Орхан Гасанлы @azerphoenix Куратор тега Java
Java Software Engineer

Здравствуйте!
Не являюсь экспертом в этой области, но если вам нужно именно в Java, то могу порекомендовать вам следующие движки и фреймворки:

Desktop/Android/BlackBerry/iOS/HTML5 Java game development framework
https://libgdx.badlogicgames.com

Но если желаете профессионально заниматься разработкой игр, то насколько мне известно, сейчас это:
Unity (ЯП — C#)
Unreal Engine (ЯП — C++)

Создание движка для 3D-рендеринга на Java

Обложка поста Создание движка для 3D-рендеринга на Java

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

Многие разработчики ошибочно полагают, что создание даже простейшего 3D-приложения с нуля требует нечеловеческих знаний и усилий. К счастью, это не совсем так. Более того, при наличии компьютера и свободного времени, можно создать нечто подобное самостоятельно. Давайте взглянем на процесс разработки нашего собственного движка для 3D-рендеринга.

Итак, для чего же это всё нужно? Во-первых, создание движка для 3D-рендеринга поможет понять, как же работают современные движки изнутри. Во-вторых, сам движок при желании можно использовать и в своём собственном приложении, не прибегая к вызову внешних зависимостей. В случае с Java это значит, что вы можете создать своё собственное приложение для просмотра 3D-изображений без зависимостей (далёких от API Java), которое будет работать практически везде и уместится в 50 КБ!

Само собой, если вы хотите создать какое-нибудь большое 3D-приложение с плавной анимацией, вам лучше использовать OpenGL/WebGL. Однако, имея базовое представление о том, как устроены подобные движки, работа с более сложными движками будет казаться в разы проще.

В этой статье я постараюсь объяснить базовый 3D-рендеринг с ортографической проекцией, простую треугольную растеризацию (процесс, обратный векторизации), Z-буферизацию и плоское затенение. Я не буду заострять своё внимание на таких вещах, как оптимизация, текстуры и разные настройки освещения — если вам это нужно, попробуйте использовать более подходящие для этого инструменты, вроде OpenGL (существует множество библиотек, позволяющих вам работать с OpenGL, даже используя Java).

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

Довольно болтать — давайте приступим к делу!

GUI

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

import javax.swing.*; import java.awt.*; public class DemoViewer < public static void main(String[] args) < JFrame frame = new JFrame(); Container pane = frame.getContentPane(); pane.setLayout(new BorderLayout()); // slider to control horizontal rotation JSlider headingSlider = new JSlider(0, 360, 180); pane.add(headingSlider, BorderLayout.SOUTH); // slider to control vertical rotation JSlider pitchSlider = new JSlider(SwingConstants.VERTICAL, -90, 90, 0); pane.add(pitchSlider, BorderLayout.EAST); // panel to display render results JPanel renderPanel = new JPanel() < public void paintComponent(Graphics g) < Graphics2D g2 = (Graphics2D) g; g2.setColor(Color.BLACK); g2.fillRect(0, 0, getWidth(), getHeight()); // rendering magic will happen here >>; pane.add(renderPanel, BorderLayout.CENTER); frame.setSize(400, 400); frame.setVisible(true); > > 

Результат должен выглядеть вот так:

Создание движка для 3D-рендеринга на Java 1

Теперь давайте добавим некоторые модели — вершины и треугольники. Вершина — это просто структура для хранения наших трёх координат (X, Y и Z), а треугольник соединяет вместе три вершины и содержит их цвет.

class Vertex < double x; double y; double z; Vertex(double x, double y, double z) < this.x = x; this.y = y; this.z = z; >> class Triangle < Vertex v1; Vertex v2; Vertex v3; Color color; Triangle(Vertex v1, Vertex v2, Vertex v3, Color color) < this.v1 = v1; this.v2 = v2; this.v3 = v3; this.color = color; >> 

Здесь я буду считать, что X означает перемещение влево-вправо, Y — вверх-вниз, а Z будет глубиной (так, что ось Z перпендикулярна вашему экрану). Положительная Z будет означать «ближе к пользователю».

В качестве примера я выбрал тетраэдр как простейшую фигуру, о которой вспомнил — нужно всего 4 треугольника, чтобы описать её.

Код также будет достаточно простым — мы просто создаём 4 треугольника и добавляем их в ArrayList:

List tris = new ArrayList<>(); tris.add(new Triangle(new Vertex(100, 100, 100), new Vertex(-100, -100, 100), new Vertex(-100, 100, -100), Color.WHITE)); tris.add(new Triangle(new Vertex(100, 100, 100), new Vertex(-100, -100, 100), new Vertex(100, -100, -100), Color.RED)); tris.add(new Triangle(new Vertex(-100, 100, -100), new Vertex(100, -100, -100), new Vertex(100, 100, 100), Color.GREEN)); tris.add(new Triangle(new Vertex(-100, 100, -100), new Vertex(100, -100, -100), new Vertex(-100, -100, 100), Color.BLUE)); 

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

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

g2.translate(getWidth() / 2, getHeight() / 2); g2.setColor(Color.WHITE); for (Triangle t : tris)

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

Создание движка для 3D-рендеринга на Java 3

Вы можете не поверить, но это наш тетраэдр в ортогональной проекции, честно!

Теперь нам нужно добавить вращение. Для этого мне нужно будет немного отойти от темы и поговорить об использовании матриц и о том, как с их помощью достичь трёхмерной трансформации 3D-точек.

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

Возьмём наш входной вектор A:

И умножим его на так называемую матрицу трансформации T, чтобы получить в итоге выходной вектор B:

Например, вот как будет выглядеть трансформация, если мы умножим на 2:

Вы не можете описать любую возможную трансформацию, используя матрицы размера 3×3 — например, если переход происходит за пределы пространства. Вы можете использовать матрицы размера 4×4, делая перекос в 4D-пространство, но об этом не в этой статье.

Трансформации, которые нам пригодятся здесь — масштабирование и вращение.

Любое вращение в 3D-пространстве может быть выражено в 3 примитивных вращениях: вращение в плоскости XY, вращение в плоскости YZ и вращение в плоскости XZ. Мы можем записать матрицы трансформации для каждого из данных вращений следующим путём:

  • Матрица вращения XY:
  • Матрица вращения YZ:
  • Матрица вращения XZ:

И вот здесь начинается магия: если вам нужно сначала совершить вращение точки в плоскости XY, используя матрицу трансформации T1, и затем совершить вращение этой точки в плоскости YZ, используя матрицу трансформации T2, то вы можете просто умножить T1 на T2 и получить одну матрицу, которая опишет всё вращение:

Это очень полезная оптимизация — вместо того, чтобы постоянно считать вращения на каждой точке, мы заранее считаем одну матрицу и затем используем её.

Что ж, довольно страшной математики, давайте вернёмся к коду. Создадим служебный класс Matrix3, который будет обрабатывать перемножения типа “матрица-матрица” и “вектор-матрица”:

class Matrix3 < double[] values; Matrix3(double[] values) < this.values = values; >Matrix3 multiply(Matrix3 other) < double[] result = new double[9]; for (int row = 0; row < 3; row++) < for (int col = 0; col < 3; col++) < for (int i = 0; i < 3; i++) < result[row * 3 + col] += this.values[row * 3 + i] * other.values[i * 3 + col]; >> > return new Matrix3(result); > Vertex transform(Vertex in) < return new Vertex(in.x * values[0] + in.y * values[3] + in.z * values[6], in.x * values[1] + in.y * values[4] + in.z * values[7], in.x * values[2] + in.y * values[5] + in.z * values[8]); >> 

Теперь можно и оживить наши скроллеры вращения. Горизонтальный скроллер будет контролировать вращение влево-вправо (XZ), а вертикальный скроллер будет контролировать вращение вверх-вниз (YZ).

Давайте создадим нашу матрицу вращения:

double heading = Math.toRadians(headingSlider.getValue()); Matrix3 transform = new Matrix3(new double[] ); g2.translate(getWidth() / 2, getHeight() / 2); g2.setColor(Color.WHITE); for (Triangle t : tris)

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

headingSlider.addChangeListener(e -> renderPanel.repaint()); pitchSlider.addChangeListener(e -> renderPanel.repaint()); 

Как вы, наверное, уже заметили, вращение вверх-вниз ещё не работает. Добавим эти строки в код:

Matrix3 headingTransform = new Matrix3(new double[] ); double pitch = Math.toRadians(pitchSlider.getValue()); Matrix3 pitchTransform = new Matrix3(new double[] ); Matrix3 transform = headingTransform.multiply(pitchTransform); 

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

Я буду использовать очень простой, но крайне неэффективный метод — растеризация через барицентрические координаты. Настоящие 3D-движки используют растеризацию, задействуя железо компьютера, что очень быстро и эффективно, но мы не можем использовать нашу видеокарту, так что будем делать всё вручную, через код.

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

BufferedImage img = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); for (Triangle t : tris) < Vertex v1 = transform.transform(t.v1); Vertex v2 = transform.transform(t.v2); Vertex v3 = transform.transform(t.v3); // since we are not using Graphics2D anymore, we have to do translation manually v1.x += getWidth() / 2; v1.y += getHeight() / 2; v2.x += getWidth() / 2; v2.y += getHeight() / 2; v3.x += getWidth() / 2; v3.y += getHeight() / 2; // compute rectangular bounds for triangle int minX = (int) Math.max(0, Math.ceil(Math.min(v1.x, Math.min(v2.x, v3.x)))); int maxX = (int) Math.min(img.getWidth() - 1, Math.floor(Math.max(v1.x, Math.max(v2.x, v3.x)))); int minY = (int) Math.max(0, Math.ceil(Math.min(v1.y, Math.min(v2.y, v3.y)))); int maxY = (int) Math.min(img.getHeight() - 1, Math.floor(Math.max(v1.y, Math.max(v2.y, v3.y)))); double triangleArea = (v1.y - v3.y) * (v2.x - v3.x) + (v2.y - v3.y) * (v3.x - v1.x); for (int y = minY; y = 0 && b1 = 0 && b2 = 0 && b3 > > > g2.drawImage(img, 0, 0, null); 

Довольно много кода, но теперь у нас есть цветной тетраэдр на экране.

Создание движка для 3D-рендеринга на Java 4

Если вы поиграетесь с демкой, то вы заметите, что не всё сделано идеально — например, синий треугольник всегда выше других. Так происходит потому, что мы отрисовываем наши треугольники один за другим. Синий здесь — последний, поэтому он отрисовывается поверх других.

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

double[] zBuffer = new double[img.getWidth() * img.getHeight()]; // initialize array with extremely far away depths for (int q = 0; q < zBuffer.length; q++) < zBuffer[q] = Double.NEGATIVE_INFINITY; >for (Triangle t : tris) < // handle rasterization. // for each rasterized pixel: double depth = b1 * v1.z + b2 * v2.z + b3 * v3.z; int zIndex = y * img.getWidth() + x; if (zBuffer[zIndex] < depth) < img.setRGB(x, y, t.color.getRGB()); zBuffer[zIndex] = depth; >> 

Теперь видно, что у нашего тетраэдра есть одна белая сторона:

Создание движка для 3D-рендеринга на Java 5

Вот мы и получили работающий движок для 3D-рендеринга!

Но и это ещё не конец. В реальном мире восприятие какого-либо цвета меняется в зависимости от положения источников света — если на поверхность падает лишь небольшое количество света, то она видится более темной.

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

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

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

Векторное произведение — это бинарная операция на двух векторах, которые определены в 3D пространстве вот так:

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

Создание движка для 3D-рендеринга на Java 6

for (Triangle t : tris) < // transform vertices before calculating normal. Vertex norm = new Vertex(ab.y * ac.z - ab.z * ac.y, ab.z * ac.x - ab.x * ac.z, ab.x * ac.y - ab.y * ac.x); double normalLength = Math.sqrt(norm.x * norm.x + norm.y * norm.y + norm.z * norm.z); norm.x /= normalLength; norm.y /= normalLength; norm.z /= normalLength; > 

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

Косинус угла между векторами можно посчитать по формуле:

Где ||A|| — длина вектора, а числитель — скалярное произведение векторов A и B:

Обратите внимание на то, что длина вектора направления света равна 1, так же, как и длина нормали треугольника (мы уже нормализовали это). Таким образом, формула просто превращается в это:

Заметьте, что только Z компонент направления света не равен нулю, так что мы можем просто всё упростить:

В коде это всё выглядит тривиально:

double angleCos = Math.abs(norm.z); 

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

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

public static Color getShade(Color color, double shade)

Код будет давать нам некоторые эффекты затенения, но спадать они будут куда быстрее, чем нам нужно. Так происходит, потому что в Java используется спектр цветов sRGB.

Так что нам нужно конвертировать каждый цвет в линейный формат, применить затенение и затем конвертировать обратно. Реальный переход из sRGB к линейному RGB — довольно трудоёмкий процесс, так что я не буду выполнять полный перечень задач здесь. Вместо этого, я сделаю нечто приближенное к этому.

public static Color getShade(Color color, double shade) < double redLinear = Math.pow(color.getRed(), 2.4) * shade; double greenLinear = Math.pow(color.getGreen(), 2.4) * shade; double blueLinear = Math.pow(color.getBlue(), 2.4) * shade; int red = (int) Math.pow(redLinear, 1/2.4); int green = (int) Math.pow(greenLinear, 1/2.4); int blue = (int) Math.pow(blueLinear, 1/2.4); return new Color(red, green, blue); > 

И теперь мы видим, как наш тетраэдр оживляется. У нас есть работающий движок для 3D рендеринга с цветами, освещением, затенением, и заняло это около 200 строк кода — неплохо!

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

public static List inflate(List tris) < List result = new ArrayList<>(); for (Triangle t : tris) < Vertex m1 = new Vertex((t.v1.x + t.v2.x)/2, (t.v1.y + t.v2.y)/2, (t.v1.z + t.v2.z)/2); Vertex m2 = new Vertex((t.v2.x + t.v3.x)/2, (t.v2.y + t.v3.y)/2, (t.v2.z + t.v3.z)/2); Vertex m3 = new Vertex((t.v1.x + t.v3.x)/2, (t.v1.y + t.v3.y)/2, (t.v1.z + t.v3.z)/2); result.add(new Triangle(t.v1, m1, m3, t.color)); result.add(new Triangle(t.v2, m1, m2, t.color)); result.add(new Triangle(t.v3, m2, m3, t.color)); result.add(new Triangle(m1, m2, m3, t.color)); >for (Triangle t : result) < for (Vertex v : new Vertex[] < t.v1, t.v2, t.v3 >) < double l = Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z) / Math.sqrt(30000); v.x /= l; v.y /= l; v.z /= l; >> return result; > 

Вот что должно у вас получиться:

Создание движка для 3D-рендеринга на Java 7

Я бы закончил эту статью, порекомендовав одну занимательную книгу: “Основы 3D-математики для графики и разработки игр”. В ней вы можете найти детальное объяснение процесса рендеринга и математики в этом процессе. Её стоит прочитать, если вам интересны движки для рендеринга.

Создание игры на Java: основы, примеры и пошаговая инструкция

Создание игры на Java: основы, примеры и пошаговая инструкция

Даниил Шатухин

Даниил Шатухин Редактор в «Хабре», тестировщик и любитель веба

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

Освойте профессию «Java-разработчик»

Java для разработки игр

Долгое время Java был основным языком для разработки мобильных игр под сотовые телефоны, потом язык стал сердцем системы Android. Поэтому множество проектов из начала двухтысячных годов используют под капотом Java. К примеру, культовая игра Gravity Defied, в которой надо помочь мотоциклисту проехаться по холмам и не упасть, разработана на Java. java игра Gravity DefiedПример десктопного проекта игры на Java — Minecraft, основная версия полностью написана на этом языке. Позже Microsoft переписала проект на C++ и выпустила Bedrock Edition, но все равно продолжает поддерживать версию на Java, которая обеспечивает большую совместимость, особенно на старом железе. Джава игра MinecraftJava можно использовать не только для изучения основ геймдева, но и для разработки больших успешных проектов, в которые играют миллионы пользователей.

Профессия / 14 месяцев
Java-разработчик
Освойте востребованный язык
3 933 ₽/мес 7 150 ₽/мес

Group 1321314345 (4)

Игровые движки на Java

  • jMonkeyEngine — высокопроизводительный движок для 3D-игр. Полностью поддерживает работу с драйвером для 3D-графики OpenGL и содержит в себе множество готовых реализаций для физических симуляций и визуализаций. Код проекта открыт, а использовать jMonkeyEngine можно бесплатно.
  • LibGDX — фреймворк для создания игр, написанный на Java, но некоторые части реализованы на C++. Проект кроссплатформенный, поэтому с его помощью можно создавать игры для Windows, Linux, RaspberryOS, macOS, Android, iOS и браузера. В основном его используют для мобильных 2D-игр.
  • LWJGL (Lightweight Java Game Library) — мощный 3D-движок для высокоэффективных проектов. Поддерживает работу с Vulkan, OpenGL и другими открытыми графическими API. Есть все необходимое для сборки готовых проектов под Windows, macOS, Linux и VR-гарнитуры Oculus. Именно на этом движке написали Java-версию Minecraft.

Правила игры

Теперь мы знаем практически всё, что нужно для создания собственной игры на Java. Делать ее будем с помощью графической библиотеки Swing. Она уже входит в Java и помогает создавать интерфейсы для окон и виджетов. Это не профессиональный движок, но он позволяет реализовать простые игры.

Для начала обозначим правила:

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

Разработчик игр на Unity – одна
из самых творческих профессий в IT. Создайте виртуальные миры уже через полгода обучения

Пишем код

Для начала создадим класс, в котором будем хранить все необходимые переменные. Сразу зададим им начальные значения. Некоторые из них будут меняться по ходу игры и возвращаться в исходное значение после завершения. В нашем классе SimpleGame наследуется от JPanel, который помогает выстраивать интерфейс:

public class SimpleGame extends JPanel implements ActionListener, KeyListener < private int playerX = 175; // Начальное положение игрока по горизонтали private int playerY = 480; // Начальное положение игрока по вертикали private int playerSpeed = 15; // Скорость движения игрока private ArrayListenemyX = new ArrayList<>(); // X-координаты врагов private ArrayList enemyY = new ArrayList<>(); // Y-координаты врагов private int enemySpeed = 20; // Скорость движения врагов private Timer timer; // Таймер для обновления экрана private boolean gameOver = false; // Флаг окончания игры private int score = 0; // Счет игрока

Теперь создадим класс игры SimpleGame, в котором обозначим настройки. Вместе с этим создадим таймер с интервалом 100 миллисекунд и запустим его:

public SimpleGame() < addKeyListener(this); setFocusable(true); setFocusTraversalKeysEnabled(false); timer = new Timer(100, this); // Тут создаем таймер timer.start(); // В этой строчке его запускаем >

Код на Java не запустится, если в нем нет главного класса main, поэтому создадим его и обозначим в нем настройки окна. Название укажем в frame, а размеры — в setSize:

public static void main(String[] args)

Все подготовительные этапы выполнены, и можно переходить к отрисовке объектов на экране. Для этого используем возможности Swing. Фон зальем черным цветом, а объект игрока — белым. В качестве обозначения цветов можно использовать их англоязычные названия, но заглавными буквами. К примеру, BLACK, WHITE, RED:

public void paintComponent(Graphics g) < super.paintComponent(g); g.setColor(Color.BLACK); // Заливаем фон черным цветом g.fillRect(0, 0, 400, 600); g.setColor(Color.WHITE); // Белый цвет для фигуры игрока g.fillRect(playerX, playerY, 50, 50); // Рисуем объект игрока

Интерфейс игры на java

Фон и игрок на экране уже есть, но теперь не хватает врагов. Отрисуем их в верхней части окна и отобразим с помощью красных кругов. Вражеских объектов на игровом поле может быть несколько одновременно, поэтому выводить их будем с помощью цикла for:

for (int i = 0; i < enemyX.size(); i++) < g.setColor(Color.RED); // Используем красный цвет g.fillOval(enemyX.get(i), enemyY.get(i), 20, 20); >

Интерфейс игры на java со счетом и врагами

Теперь закончим основной интерфейс надписями на экране. В верхнем левом углу будем выводить актуальный счет, а после проигрыша по центру будем показывать надпись «Конец игры». Их сделаем белым цветом и выберем шрифт Arial. В конце игры будем останавливать игровой таймер:

g.setColor(Color.WHITE); g.setFont(new Font("Arial", Font.PLAIN, 20)); g.drawString("Счет: " + score, 10, 30); // Выводим счет игрока на экран if (gameOver) < g.setFont(new Font("Arial", Font.PLAIN, 40)); g.drawString("Конец игры", 120, 300); // Выводим надпись "Конец игры" при окончании игры timer.stop(); // Останавливаем таймер >

Интерфейс для завершения игры

Пришло время оживить врагов. Их объекты должны спускаться вниз и удаляться при пересечении нижней линии окна. В этот же момент счет игрока должен увеличиваться на число, равное количеству вражеских объектов. Все это реализуем в классе actionPerformed. В конце обязательно обновляем экран. Иначе враги будут оставаться на нем и копиться:

@Override public void actionPerformed(ActionEvent e) < if (!gameOver) < for (int i = 0; i < enemyX.size(); i++) < enemyY.set(i, enemyY.get(i) + enemySpeed); // Двигаем врагов вниз по экрану if (enemyY.get(i) >= 600) < enemyX.remove(i); enemyY.remove(i); score++; // Увеличиваем счет при уничтожении врага >> repaint(); // Обновляем экран

Добавим механизм, который будет создавать на экране новых врагов, если на нем никого нет. В конце добавим проверку cтолкновений с объектом игрока:

if (enemyX.isEmpty()) < spawnEnemy(); // Создаем нового врага, если текущих нет на экране >checkCollision(); // Проверяем коллизию игрока с врагами

Мы уже реализовали правила рендеринга врагов и механизм их спуска вниз, но все еще не умеем отслеживать их количество на экране. В классе spawnEnemy сразу обозначим, что одновременно на экране может быть не меньше одного и не больше пяти вражеских объектов. Можно поэкспериментировать с количеством, меняя значение переменной numEnemies:

public void spawnEnemy() < Random rand = new Random(); int numEnemies = rand.nextInt(5) + 1; // Генерируем от 1 до 5 врагов за раз for (int i = 0; i < numEnemies; i++) < int x = rand.nextInt(350); // Генерируем случайную X-координату для врага int y = 0; enemyX.add(x); enemyY.add(y); // Добавляем врага в списки координат >>

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

Для отслеживания столкновений важно знать границы двух объектов. Если они соприкасаются, то запускается определенное действие. К примеру, если границы двух объектов-автомобилей в игре сходятся, то начинает воспроизводиться анимация столкновения и разрушений. В нашем случае игра будет завершаться, а на экран выводится надпись «Конец игры»:

public void checkCollision() < Rectangle playerBounds = new Rectangle(playerX, playerY, 50, 50); // Границы игрока for (int i = 0; i < enemyX.size(); i++) < Rectangle enemyBounds = new Rectangle(enemyX.get(i), enemyY.get(i), 20, 20); // Границы врага if (playerBounds.intersects(enemyBounds)) < gameOver = true; // Если произошло столкновение, игра заканчивается break; >> >

Пришла пора оживить игрока. Главный объект может двигаться только влево и вправо по горизонтали. Это упрощает нам задачу, так как не надо реализовывать дополнительные модели управления. Для перемещения будем использовать клавиши стрелок, которые обозначаются как VK_LEFT и VK_RIGHT. При нажатии просто будем менять координаты объекта игрока по оси x:

@Override public void keyPressed(KeyEvent e) < int key = e.getKeyCode(); if (!gameOver) < if (key == KeyEvent.VK_LEFT && playerX >0) < playerX -= playerSpeed; // Перемещаем игрока влево >if (key == KeyEvent.VK_RIGHT && playerX < 350) < playerX += playerSpeed; // Перемещаем игрока вправо >> >

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

import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.ArrayList; import java.util.Random; public class SimpleGame extends JPanel implements ActionListener, KeyListener < private int playerX = 175; // Начальное положение игрока по горизонтали private int playerY = 480; // Начальное положение игрока по вертикали private int playerSpeed = 15; // Скорость движения игрока private ArrayListenemyX = new ArrayList<>(); // X-координаты врагов private ArrayList enemyY = new ArrayList<>(); // Y-координаты врагов private int enemySpeed = 20; // Скорость движения врагов private Timer timer; // Таймер для обновления экрана private boolean gameOver = false; // Флаг окончания игры private int score = 0; // Счет игрока public SimpleGame() < addKeyListener(this); setFocusable(true); setFocusTraversalKeysEnabled(false); timer = new Timer(100, this); // Тут создаем таймер timer.start(); // В этой строчке его запускаем >public static void main(String[] args) < JFrame frame = new JFrame("Simple Game"); SimpleGame game = new SimpleGame(); frame.add(game); frame.setSize(400, 600); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); >public void paintComponent(Graphics g) < super.paintComponent(g); g.setColor(Color.BLACK); // Заливаем фон черным цветом g.fillRect(0, 0, 400, 600); g.setColor(Color.WHITE); // Белый цвет для фигуры игрока g.fillRect(playerX, playerY, 50, 50); // Рисуем объект игрока for (int i = 0; i < enemyX.size(); i++) < g.setColor(Color.RED); // Используем красный цвет g.fillOval(enemyX.get(i), enemyY.get(i), 20, 20); >g.setColor(Color.WHITE); g.setFont(new Font("Arial", Font.PLAIN, 20)); g.drawString("Счет: " + score, 10, 30); // Выводим счет игрока на экран if (gameOver) < g.setFont(new Font("Arial", Font.PLAIN, 40)); g.drawString("Конец игры", 120, 300); // Выводим надпись "Конец игры" при окончании игры timer.stop(); // Останавливаем таймер >> @Override public void actionPerformed(ActionEvent e) < if (!gameOver) < for (int i = 0; i < enemyX.size(); i++) < enemyY.set(i, enemyY.get(i) + enemySpeed); // Двигаем врагов вниз по экрану if (enemyY.get(i) >= 600) < enemyX.remove(i); enemyY.remove(i); score++; // Увеличиваем счет при уничтожении врага >> repaint(); // Перерисовываем экран if (enemyX.isEmpty()) < spawnEnemy(); // Создаем нового врага, если текущих нет на экране >checkCollision(); // Проверяем коллизию игрока с врагами > > public void spawnEnemy() < Random rand = new Random(); int numEnemies = rand.nextInt(5) + 1; // Генерируем от 1 до 5 врагов за раз for (int i = 0; i < numEnemies; i++) < int x = rand.nextInt(350); // Генерируем случайную X-координату для врага int y = 0; enemyX.add(x); enemyY.add(y); // Добавляем врага в списки координат >> public void checkCollision() < Rectangle playerBounds = new Rectangle(playerX, playerY, 50, 50); // Границы игрока for (int i = 0; i < enemyX.size(); i++) < Rectangle enemyBounds = new Rectangle(enemyX.get(i), enemyY.get(i), 20, 20); // Границы врага if (playerBounds.intersects(enemyBounds)) < gameOver = true; // Если произошло столкновение, игра заканчивается break; >> > @Override public void keyTyped(KeyEvent e) <> @Override public void keyPressed(KeyEvent e) < int key = e.getKeyCode(); if (!gameOver) < if (key == KeyEvent.VK_LEFT && playerX >0) < playerX -= playerSpeed; // Перемещаем игрока влево >if (key == KeyEvent.VK_RIGHT && playerX < 350) < playerX += playerSpeed; // Перемещаем игрока вправо >> > @Override public void keyReleased(KeyEvent e) <> >

Что дальше

У нас есть готовая игра, которую можно модифицировать, вот пара идей для вдохновения:

  • в проект можно добавить фоновую музыку и звуки, которые будут воспроизводиться при движении игрока и проигрыше. Так игра получится увлекательнее и живее;
  • уворачиваться от вражеских объектов со временем надоедает, поэтому для разнообразия игрового процесса можно добавить возможность уничтожать врагов с помощью стрельбы. Для этого надо реализовать класс пуль, их внешний вид и физику движения. При столкновении с врагом с экрана должны пропадать сам враг и пуля;
  • вместе с врагами случайно могут спускаться различные бонусы, к примеру, дополнительная жизнь или щит от врагов на какое-то время;
  • можно добавить возможность обменивать накопленные очки на дополнительные способности, облегчающие игру. К примеру, позиции спуска следующих игроков могут подсвечиваться, чтобы у игрока была возможность подготовиться.

Java-разработчик

Java уже 20 лет в мировом топе языков программирования. На нем создают сложные финансовые сервисы, стриминги и маркетплейсы. Освойте технологии, которые нужны для backend-разработки, за 14 месяцев.

Java - как делать 3D игры?

Как сделать 3D игру на языке программирования Java, можете написать сами, либо скинуть ссылку на статью или видео-урок по этой теме.

Лучший ответ

Для создания 3D игр на языке Java вам необходимо использовать специальные библиотеки, которые позволяют работать с графикой и 3D-моделями. Вот некоторые из наиболее популярных библиотек для создания 3D игр на Java:

jMonkeyEngine: Это одна из наиболее популярных библиотек для создания 3D игр на Java. jMonkeyEngine предоставляет обширный набор инструментов для создания игр, включая инструменты для работы с физикой, анимацией, звуком и сценами.

LWJGL: Эта библиотека предоставляет низкоуровневый доступ к OpenGL и другим графическим API. Она также включает в себя инструменты для работы с звуком и вводом пользовательских действий.

Java 3D: Эта библиотека предназначена специально для создания 3D-графики и предоставляет набор инструментов для работы с графикой, анимацией и моделированием.

JOGL: Эта библиотека предоставляет доступ к OpenGL на языке Java. JOGL позволяет создавать игры с высокой производительностью и высоким качеством графики.

Выбор конкретной библиотеки зависит от ваших индивидуальных потребностей и уровня опыта. Однако, с использованием любой из этих библиотек, вам понадобится изучить основы 3D-графики и математики, такие как векторы, матрицы и преобразования. Кроме того, вам может потребоваться изучить язык шейдеров для создания эффектов и текстур.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *