Пишем игру на движке JECP

от
GameDev    java, android, cross platform

Хочу поделиться статьёй о том, как писать игры под разные платформы на движке JECP.

JECP - открытый движок для создания игр и приложений для Java ME, Android и PC (Java SE). Страница проекта на GitHub.

Для начала скачаем библиотеку. Можно скачать исходники и закинуть их в проект или скачать собранную библиотеку в jar. Рекомендую второй вариант, чтобы не путаться в куче исходных файлов.

Теперь создадим несколько проектов в NetBeans. Рекомендую создать отдельную папку для этих проектов. Название папки - Pipes.
Первый: Java ME - Mobile Class Library. Это будет главным проектом, в котором мы будем писать код с использованием JECP. Название - Pipes-Core.
Второй: Java ME - Mobile Application. Сборка для Java ME. Название - Pipes-ME.
Третий: Java - Java Application. Сборка для PC. Название - Pipes-SE.
С Android в NetBeans работать сложновато, поэтому мы потом соберём всё в Eclipse IDE.

Структура папок после создания будет такова.
  jecp-1.png

В папку libs кидаем файлы библиотеки: JECP-Lib.jar, JECP-ME.jar, JECP-SE.jar.
Теперь надо подключить эти библиотеки к проектам.
К проекту Pipes-Core подключаем JECP-Lib. В настройках Java ME проекта во вкладке Build - Libraries & Resources нажимаем Add Jar/Zip и выбираем файл. Напротив пути убираем галочку Package, чтобы библиотека не включалась в сборку.
  jecp-2.png
К проекту Pipes-ME подключаем JECP-Lib, JECP-ME и проект Pipes-Core. Чтобы добавить проект в виде библиотеки, всё также заходим в настройках проекта во вкладку Build - Libraries & Resources и нажимаем Add Project, после чего выбираем Pipes-Core.
Наконец, для Pipes-SE подключаем JECP-Lib, JECP-SE и проект Pipes-Core.
  jecp-3.png

Осталось только создать главные классы для каждого проекта.
Создадим в главном проекте Pipes-Core класс MainApp.
  1. package pipes;
  2.  
  3. import com.annimon.jecp.ApplicationListener;
  4. import com.annimon.jecp.Graphics;
  5.  
  6. public class MainApp implements ApplicationListener {
  7.  
  8.     public void onStartApp(int width, int height) { }
  9.  
  10.     public void onPauseApp() { }
  11.  
  12.     public void onDestroyApp() { }
  13.  
  14.     public void onPaint(Graphics g) { }
  15.  
  16.     public void onUpdate() { }
  17. }

В проекте Pipes-ME класс Pipes.
  1. import com.annimon.jecp.me.Application;
  2. import pipes.MainApp;
  3.  
  4. public class Pipes extends Application {
  5.  
  6.     public Pipes() {
  7.         super(new MainApp());
  8.     }    
  9. }

И в Pipes-SE класс Pipes.
  1. import com.annimon.jecp.se.Application;
  2. import pipes.MainApp;
  3.  
  4. public class Pipes extends Application {
  5.  
  6.     public static void main(String[] args) {
  7.         new Pipes();
  8.     }
  9.  
  10.     public Pipes() {
  11.         super(new MainApp(), 240, 320);
  12.     }
  13. }
Теперь при запуске Java ME или Java SE приложения будет скомпилирован проект Pipes-Core и скомпонован с главным классом.
Подготовительная часть на этом закончена. Кажется, что долго, но на самом деле всё сводится к созданию проектов и подключению библиотек. Структура папок выглядит так
  jecp-4.png

После настройки проекта, можно писать код в Pipes-Core.
Нам нужно работать с клавиатурой и мышью, поэтому имплементируем интерфейс InputListener:
public class MainApp implements ApplicationListener, InputListener {
Теперь сохраним ширину и высоту и укажем ссылку на InputListener в методе onStartApp:
  1. private int width, height;
  2.  
  3. public void onStartApp(int width, int height) {
  4.     this.width = width;
  5.     this.height = height;
  6.     Jecp.inputListener = this;
  7. }

Кратко об основных методах.
onStartApp - стартовый метод. В нём нужно проводить инициализацию объектов.
onPaint - здесь будет отрисовка.
onUpdate - в этом методе нужно проводить различные вычисления.
onKeyPressed - вызывается при нажатии клавиши.
onPointerPressed - вызывается при нажатии кнопки мыши или нажатию на сенсорном экране.

Теперь можно писать игру. Суть игры "Трубы" состоит в том, чтобы за определённое время соединить трубы с верхней левой ячейки к нижней правой.
Ячейки бывают нескольких типов - горизонтальные, вертикальные и угловые.
Мест соединения у ячейки две. Для горизонтальной трубы они будут слева и справа, для вертикальной - сверху и снизу, для угловой - слева+сверху, слева+снизу, справа+сверху и справа+снизу.
  jecp-5.png
По нажатию на ячейку, она будет меняться. Горизонтальная станет вертикальной, вертикальная - горизонтальной, а угловая повернётся на 90 градусов.
Исходя из вышеперечисленного можно создать абстрактный класса Cell, наследники которого будут задавать тип трубы, тип трубы при повороте и доступные места соединения.
  1. public abstract class Cell {
  2.  
  3.     public abstract int type();
  4.  
  5.     protected abstract int nextCellType();
  6.  
  7.     public abstract void draw(Graphics g, int cellSize, int row, int column);
  8.  
  9.     public boolean supportLeft() {
  10.         return false;
  11.     }
  12.  
  13.     public boolean supportRight() {
  14.         return false;
  15.     }
  16.  
  17.     public boolean supportUp() {
  18.         return false;
  19.     }
  20.  
  21.     public boolean supportDown() {
  22.         return false;
  23.     }
  24. }

Теперь для каждого типа трубы создадим класс. Пример угловой трубы:
  1. public final class LeftToDownCell extends Cell {
  2.  
  3.     public int type() {
  4.         return LEFT_TO_DOWN;
  5.     }
  6.  
  7.     protected int nextCellType() {
  8.         return LEFT_TO_UP;
  9.     }
  10.  
  11.     public void draw(Graphics g, int cellSize, int row, int col) {
  12.         g.fillRect(x, y + c2 - size, c2, size * 2);
  13.         g.fillRect(x + c2 - size, y + c2 - size, size * 2, c2 + size);
  14.     }
  15.  
  16.     public boolean supportLeft() {
  17.         return true;
  18.     }
  19.  
  20.     public boolean supportDown() {
  21.         return true;
  22.     }
  23. }

Так как таких клеток у нас будет много, то чтобы каждый раз не создавать вручную объект класса Cell, можно написать класс, который будет хранить по одному объекту. Тем самым будет экономиться память.
  1. public class CellBuilder {
  2.  
  3.     private static final Cell HORIZONTAL = new HorizontalCell();
  4.     private static final Cell VERTICAL = new VerticalCell();
  5.     private static final Cell LEFT_TO_DOWN = new LeftToDownCell();
  6.     private static final Cell LEFT_TO_UP = new LeftToUpCell();
  7.     private static final Cell RIGHT_TO_UP = new RightToUpCell();
  8.     private static final Cell RIGHT_TO_DOWN = new RightToDownCell();
  9.  
  10.     public static Cell byType(int type) {
  11.         switch (type) {
  12.             case Cell.HORIZONTAL:
  13.                 return HORIZONTAL;
  14.             // ...
  15.             case Cell.RIGHT_TO_DOWN:
  16.             default:
  17.                 return RIGHT_TO_DOWN;
  18.         }
  19.     }
  20. }

Теперь создадим класс Board, который будет хранить ячейки и выполнять над ними операции.
  Board.java

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

Основные моменты:
Для удобства работы со случайными числами, используется класс JecpRandom.
Метод Keys.convertToDpad(key) преобразовывает код клавиши в код джойстика: вверх, вниз, влево, вправо, огонь. Очень удобно для игр.
Keys.wasdAsDpad = true - устанавливает кнопки WASD как кнопки джойстика. То есть можно не только стрелками управлять, но и этими клавишами. ENTER и пробел в таком случае будут кнопкой Огонь.

Теперь скомпилируем под Android.
Всё так же создаём проект, копируем библиотеки JECP-Lib, JECP-Android в папку libs, затем копируем исходники проекта Pipes-Core в папку src. MainActivity будет выглядеть так:
  1. public class MainActivity extends Application {
  2.  
  3.     @Override
  4.     protected void onCreate() {
  5.         init(new MainApp());
  6.     }
  7. }

На этом всё, игра готова.
jecp-6.png
Проект Pipes-Project.zip
Pipes-ME.jar | Pipes-SE.jar | Pipes.apk
  • +13
  • views 5498