Игру на Java обычно делают не «с нуля», а на одном из трех уровней: на фреймворке вроде libGDX (2D и простое 3D, десктоп и Android), на 3D-движке jMonkeyEngine или прямо на LWJGL — низкоуровневой обертке над OpenGL, Vulkan и OpenAL, на которой, например, работает Minecraft: Java Edition. Ниже — как выбрать уровень, как устроен игровой цикл (с кодом, который можно запустить), первый проект на libGDX и честная граница: где Java в играх уместна, а где нет.
Содержание
Версии в статье сверены на сентябрь 2026: Java 25 LTS, libGDX 1.14.2, LWJGL 3.4.3, jMonkeyEngine 3.9 (стабильная ветка).
Три слова, которые путают: библиотека, фреймворк, движок
- Библиотека дает отдельные функции, а управление программой остается у вас. LWJGL открывает окно и дает доступ к OpenGL, но где рисовать, как считать физику и когда обновлять кадр — решает ваш код.
- Фреймворк сам владеет главным циклом и вызывает ваши методы. В libGDX вы пишете
create(),render(),dispose(), а фреймворк решает, когда их вызвать. - Движок — фреймворк плюс готовые подсистемы и инструменты: граф сцены, загрузка 3D-моделей, физика, часто редактор. Так устроен jMonkeyEngine.
Граница условная: libGDX тоже называют движком, а у движков есть низкоуровневый доступ. Но на вопрос «сколько мне придется написать самому» эта шкала отвечает честно.
И еще одна путаница, которая встречается в старых подборках: Java и JavaScript — разные языки. Phaser, PixiJS, Babylon.js, PlayCanvas и A-Frame — это движки и библиотеки для JavaScript в браузере, к Java они отношения не имеют.
Какой инструмент выбрать под задачу
| Инструмент | Уровень | 2D / 3D | Платформы | Когда брать |
|---|---|---|---|---|
| libGDX | фреймворк | 2D, базовое 3D | Windows, macOS, Linux, Android, iOS, веб | первая игра, 2D, мобильные и десктоп из одного кода |
| jMonkeyEngine | движок | 3D | десктоп, Android | 3D-игра, граф сцены, физика из коробки |
| LWJGL 3 | библиотека | любое | Windows, macOS, Linux | свой движок, учеба графике, полный контроль |
| FXGL | фреймворк поверх JavaFX | 2D | десктоп | простые 2D-игры и учебные проекты на JavaFX |
Как читать таблицу: чем ниже уровень, тем больше контроля и тем больше кода вы пишете сами. Для первого проекта почти всегда разумно начать с libGDX: он берет на себя окно, ввод, звук, загрузку текстур и сборку под платформы, а игровую логику оставляет вам.
Примеры реальных проектов: Minecraft: Java Edition написан на Java и использует LWJGL (версия Bedrock для консолей и мобильных написана на C++); на libGDX сделана Slay the Spire.
Игровой цикл: сердце любой игры
Любая игра — это цикл «обработать ввод -> обновить мир -> нарисовать кадр», который крутится десятки раз в секунду. Главная ошибка новичка — двигать объекты «на столько-то пикселей за кадр». Тогда скорость игры зависит от частоты кадров компьютера.
Сначала контрпример. Объект должен пройти 120 пикселей за секунду, считаем двумя способами при разном FPS:
public class FrameBug {
public static void main(String[] args) {
for (int fps : new int[]{30, 60, 144}) {
double xWrong = 0, xRight = 0;
double dt = 1.0 / fps;
for (int frame = 0; frame < fps; frame++) { // ровно 1 секунда
xWrong += 2; // 2 пикселя за кадр
xRight += 120 * dt; // 120 пикселей в секунду
}
System.out.printf("%3d FPS: за кадр -> %.0f px, с учетом dt -> %.0f px%n", fps, xWrong, xRight);
}
}
}
Вывод (Java 25, java FrameBug.java):
30 FPS: за кадр -> 60 px, с учетом dt -> 120 px
60 FPS: за кадр -> 120 px, с учетом dt -> 120 px
144 FPS: за кадр -> 288 px, с учетом dt -> 120 px
На мониторе 144 Гц «пиксели за кадр» дают персонажа, который бегает в 2,4 раза быстрее задуманного. Исправление — умножать скорость на dt (время, прошедшее с прошлого кадра).
Для физики одного dt мало: при скачке FPS шаг становится то крупным, то мелким, и столкновения ведут себя по-разному. Поэтому логику часто обновляют фиксированным шагом, а рисуют так часто, как получается. Полный пример — мяч падает и отскакивает от пола:
public class GameLoop {
static final double STEP = 1.0 / 60.0; // шаг логики: 60 обновлений в секунду
// Состояние "игры": мяч падает и отскакивает от пола
static double y = 10.0; // высота, м
static double vy = 0.0; // скорость, м/с
static final double G = -9.8;
static void update(double dt) {
vy += G * dt;
y += vy * dt;
if (y < 0) { // столкновение с полом
y = 0;
vy = -vy * 0.5; // отскок с потерей энергии
}
}
public static void main(String[] args) {
long previous = System.nanoTime();
double accumulator = 0;
int updates = 0;
while (updates < 180) { // 3 секунды игрового времени
long now = System.nanoTime();
accumulator += (now - previous) / 1e9; // сколько реального времени прошло
previous = now;
while (accumulator >= STEP) { // догоняем логику фиксированными шагами
update(STEP);
accumulator -= STEP;
updates++;
if (updates % 30 == 0) {
System.out.printf("t=%.1f c y=%.2f м%n", updates * STEP, y);
}
}
// здесь в настоящей игре был бы render()
}
}
}
Запуск java GameLoop.java (Java 25) печатает высоту мяча каждые полсекунды:
t=0.5 c y=8.73 м
t=1.0 c y=5.02 м
t=1.5 c y=0.44 м
t=2.0 c y=2.36 м
t=2.5 c y=1.83 м
t=3.0 c y=0.37 м
Разбор по шагам:
accumulatorкопит реальное время между итерациями.- Внутренний
whileвызываетupdate(STEP)столько раз, сколько целых шагов накопилось. Медленный компьютер сделает несколько шагов за итерацию, быстрый — ни одного, но игровое время идет одинаково. - Поэтому числа в выводе не зависят от скорости машины: логика всегда делает 180 одинаковых шагов.
Граница примера: это учебная модель. Цикл крутится без паузы и загружает ядро процессора на 100%; в настоящей игре кадр ждет вертикальную синхронизацию (vsync) или спит, а между шагами логики делают интерполяцию для плавной картинки. Фреймворки вроде libGDX берут этот цикл на себя.
Первый проект на libGDX
Дорожная карта до артефакта — окна, где спрайт двигается стрелками:
- Установите JDK (для нового проекта разумно взять Java 25 LTS или ту версию, которую поддерживает текущий шаблон libGDX) и IDE: IntelliJ IDEA или Eclipse.
- Сгенерируйте проект утилитой gdx-liftoff — это рекомендуемый генератор проектов libGDX. Отметьте платформы (для начала хватит Desktop), он создаст Gradle-проект с модулями
coreиlwjgl3. - Откройте проект в IDE как Gradle-проект и запустите задачу
lwjgl3:run. Появится окно с логотипом. - Замените класс игры в модуле
coreна код ниже. - Когда игра заработает, соберите дистрибутив:
lwjgl3:jarдает запускаемый JAR, а утилитаjpackageиз JDK упаковывает приложение вместе с Java-рантаймом, чтобы игроку не нужно было ставить Java.
Класс игры в модуле core:
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.utils.ScreenUtils;
public class Main extends ApplicationAdapter {
private SpriteBatch batch;
private Texture image;
private float x = 100, y = 100;
private static final float SPEED = 200f; // пикселей в секунду
@Override
public void create() { // один раз при старте
batch = new SpriteBatch();
image = new Texture("libgdx.png"); // файл из папки assets
}
@Override
public void render() { // вызывается каждый кадр
float dt = Gdx.graphics.getDeltaTime();
if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) x += SPEED * dt;
if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) x -= SPEED * dt;
ScreenUtils.clear(0.15f, 0.15f, 0.2f, 1f);
batch.begin();
batch.draw(image, x, y);
batch.end();
}
@Override
public void dispose() { // освобождаем ресурсы GPU
batch.dispose();
image.dispose();
}
}
Что произойдет: откроется окно с темным фоном и картинкой, стрелки влево и вправо двигают ее со скоростью 200 пикселей в секунду при любом FPS — та же идея с dt, что в контрпримере выше. Метод dispose() обязателен: текстуры и SpriteBatch держат память видеокарты, сборщик мусора Java ее не освобождает.
Десктопный запускатель в модуле lwjgl3 gdx-liftoff создает сам; его суть — три строки настройки:
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration;
public class DesktopLauncher {
public static void main(String[] args) {
Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
config.setTitle("Моя первая игра");
config.setWindowedMode(800, 480);
config.useVsync(true); // кадр синхронизируется с частотой монитора
new Lwjgl3Application(new Main(), config);
}
}
Дальше по нарастающей: столкновения прямоугольников (Rectangle.overlaps), звуки (Gdx.audio), экраны меню и игры (Game и Screen), физика через расширение Box2D.
Если не получилось
| Симптом | Частая причина | Что сделать |
|---|---|---|
| Gradle падает с ошибкой версии класса или «Unsupported class file major version» | версия JDK не совпадает с той, что ждет Gradle-обертка проекта | в IDE выбрать для Gradle поддерживаемый JDK или обновить Gradle-обертку |
GdxRuntimeException: Couldn't load file: libgdx.png |
файла нет в assets или рабочая папка запуска не та |
положить файл в assets, запускать через задачу Gradle, а не «голый» main |
| На macOS окно не открывается, ошибка про главный поток | GLFW на macOS требует запуск в первом потоке | запускать шаблонный lwjgl3:run: в проекте gdx-liftoff это уже учтено |
| Персонаж на одном компьютере быстрее, чем на другом | движение «за кадр» без dt | умножать скорость на getDeltaTime() |
| Периодические подергивания кадра | много новых объектов каждый кадр, паузы сборщика мусора | переиспользовать объекты (Pool в libGDX), не создавать их в render() |
Где граница Java в геймдеве
Java хорошо подходит для инди-игр, 2D, учебных проектов, серверной части онлайн-игр и игр под Android. Но это не основной язык крупных AAA-проектов: там доминирует C++ (Unreal Engine 5 написан на нем), а в Unity 6 скрипты пишут на C#. Популярные коммерческие движки — Unity, Unreal, Godot — Java официально не поддерживают.
Причины прагматичные. Консольные SDK и основные инструменты индустрии ориентированы на C++ и C#. Сборщик мусора требует дисциплины с памятью в каждом кадре, хотя современные сборщики (ZGC, Shenandoah) заметно сократили паузы. Поэтому формула честная: на Java можно сделать законченную и коммерчески успешную игру, но путь в студию крупных проектов обычно лежит через C++ или C#.
Выводы
- Игры на Java делают на трех уровнях: фреймворк libGDX, 3D-движок jMonkeyEngine, низкоуровневая библиотека LWJGL; для первой игры проще всего libGDX.
- Phaser, PixiJS, Babylon.js и подобные — инструменты JavaScript, не Java.
- Движение считают через dt, физику — фиксированным шагом, иначе скорость игры зависит от FPS.
- Ресурсы видеокарты в libGDX освобождают вручную в
dispose(), лишние объекты в кадре вызывают подергивания. - Java уместна для инди, 2D, Android и серверов игр; в AAA основные языки — C++ и C#.
Где применяется / связь с практикой
Освойте тему на практике
Игра — хороший учебный проект по Java: в ней сразу нужны классы и наследование, коллекции, работа с временем, ресурсами и исключениями, а результат видно на экране. Чтобы уверенно писать такой код и дальше расти в разработке, стоит освоить базу языка системно — например, на курсе Java Developer. Basic. Разобрать отдельные темы Java с практикующими разработчиками можно на бесплатных открытых уроках Otus.
FAQ
Можно ли писать на Kotlin вместо Java в libGDX?
Да, libGDX работает с любым JVM-языком, а gdx-liftoff умеет создавать проект с Kotlin. Документация и примеры при этом в основном на Java.
Нужно ли игроку устанавливать Java, чтобы запустить мою игру?
Не обязательно: jpackage или jlink упаковывают игру вместе с урезанным рантаймом Java, и игрок получает обычный установщик или папку с исполняемым файлом.
Работает ли игра на libGDX в браузере?
Да, через веб-бэкенд, но с ограничениями: часть библиотек Java и рефлексии там недоступна, поэтому код для веба проверяют отдельно.



