Как стать автором
Обновить

Кроссплатформенное приложение в Android Studio на jMonkeyEngine 3

Время на прочтение 6 мин
Количество просмотров 23K
jMonkeyEngine (jME) — трёхмерный игровой движок с открытым исходным кодом. Написан на Java и использует по умолчанию LWJGL для рендеринга. Полностью поддерживаются версии OpenGL со второй по четвёртую.
Как-то раз упоминался на Хабре в далёком 2010-м году.

Помимо характеристик примечателен джавой и не очень примечателен средой разработки NetBeans, которая идёт в комплекте с SDK.

В данной статье хочу рассказать о том, как прикрутить обезьянку к привычной Android Studio.

Базовый проект


Создаём новый проект для API не ниже 8 с Blank Activity и в корневой директории проекта создаём папки core и desktop. На языке скриншотов это выглядит как:



Подразумеваем, что core — основной код приложения, а desktop и app — запускатели для десктопа и Андроида соответственно.

В корневом settings.gradle указываем эти директории:

include ':app', ':core', ':desktop'

Жмём Sync Now в правом верхнем углу окна. Это нужно будет делать при любой модификации gradle-файлов.
В директориях core и desktop создаём файл build.gradle:

apply plugin: "java"
sourceSets.main.java.srcDirs = ["src/"]

В корневой build.gradle добавляем репозиторий jmonkeyengine и минимальный набор библиотек для наших подпроектов:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.0.0'
    }
}

allprojects {
    repositories {
        jcenter()
        maven {
            url 'http://updates.jmonkeyengine.org/maven'
        } 
    }
}

project(":core") {
    apply plugin: "java"
    dependencies {
        compile 'com.jme3:jme3-core:3.0.+'
    }
}

project(":desktop") {
    apply plugin: "java"
    dependencies {
        compile project(":core")
        compile 'com.jme3:jme3-desktop:3.0.+'
        compile 'com.jme3:jme3-lwjgl:3.0.+'
    }
}

project(":app") {
    apply plugin: "android"
    dependencies {
        compile project(":core")
        compile 'com.jme3:jme3-android:3.0.+'
    }
}

После синхронизации в External Libraries должны появится указанные библиотеки со своими соратниками:



В директориях core и desktop создаём директории src, добавляем в core/src файл Game.java с содержимым из базового примера:

package org.lunapark.dev.jme3example;

import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;

public class Game extends SimpleApplication {

    @Override
    public void simpleInitApp() {
        Box b = new Box(1, 1, 1);
        Geometry geom = new Geometry("Box", b);

        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", ColorRGBA.Blue);
        geom.setMaterial(mat);

        rootNode.attachChild(geom);
    }
}

Package укажите тот же, что и при создании проекта и вылечите недуги при помощи Alt + Enter и Move to package ...:



В desktop/src создаём файл DesktopLauncher.java:

package org.lunapark.dev.jme3example;

public class DesktopLauncher {

    public static void main(String[] args) {
        Game game = new Game();
        game.start();
    }
}

Можно запустить DesktopLauncher.java и полюбоваться фантастической трёхмерной моделькой синего кубика.

Android


Для запуска на Android заменим код в MainActivity.java на код из примера (обязательно измените значение переменной appClass):

package org.lunapark.dev.jme3example;

import android.content.pm.ActivityInfo;

import com.jme3.app.AndroidHarness;
import com.jme3.system.android.AndroidConfigChooser;

import java.util.logging.Level;
import java.util.logging.LogManager;

public class MainActivity extends AndroidHarness {

    public MainActivity() {
        // Set the application class to run
        appClass = "org.lunapark.dev.jme3example.Game";

        // Try ConfigType.FASTEST; or ConfigType.LEGACY if you have problems
        eglConfigType = AndroidConfigChooser.ConfigType.BEST;

        // Exit Dialog title & message
        exitDialogTitle = "Exit?";
        exitDialogMessage = "Are you sure you want to quit?";

        // Choose screen orientation
        screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;

        // Enable MouseEvents being generated from TouchEvents (default = true)
        mouseEventsEnabled = true;

        // Set the default logging level (default=Level.INFO, Level.ALL=All Debug Info)
        LogManager.getLogManager().getLogger("").setLevel(Level.INFO);
    }
}

Дополним AndroidManifest.xml рекомендуемыми параметрами (android:launchMode=«singleTask» и блок «supports-screens»):

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.lunapark.dev.jme3example">

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:launchMode="singleTask"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

    <supports-screens
        android:anyDensity="true"
        android:largeScreens="true"
        android:normalScreens="true"
        android:smallScreens="true" />

</manifest>

Можно запустить app и созерцать кубик уже на телефоне. Если вместо кубика вы созерцаете ClassNotFound Exception и прочее NPE, скорее всего вы забыли поменять переменную appClass в MainActivity.java. Со мной так было.

Можно, конечно, в MainActivity.java заменить переменную appClass на
appClass = Game.class.getCanonicalName();

и избавиться от хардкода в переменных для диалога, но какой вы после этого индус, правда?

Ресурсы


Для использования ресурсов создадим в app директорию assets. Добавим
sourceSets { main { assets.srcDirs = ['src/main/assets', 'assets/'] } }

в app/build.gradle:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 21
    buildToolsVersion "21.1.2"

    defaultConfig {
        applicationId "org.lunapark.dev.jme3example"
        minSdkVersion 8
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    sourceSets { main { assets.srcDirs = ['src/main/assets', 'assets/'] } }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:21.0.3'
}


Создатели движка рекомендуют такую структуру каталогов:

jMonkeyProjects/MyGame/assets/Interface/   # .font, .jpg, .png, .xml
jMonkeyProjects/MyGame/assets/MatDefs/     # .j3md
jMonkeyProjects/MyGame/assets/Materials/   # .j3m
jMonkeyProjects/MyGame/assets/Models/      # .j3o
jMonkeyProjects/MyGame/assets/Scenes/      # .j3o
jMonkeyProjects/MyGame/assets/Shaders/     # .j3f, .vert, .frag
jMonkeyProjects/MyGame/assets/Sounds/      # .ogg, .wav
jMonkeyProjects/MyGame/assets/Textures/    # .jpg, .png; also .mesh.xml+.material, .mtl+.obj, .blend (!) 

Поддержим товарищей и запихнём для проверки в assets/Textures какую-нибудь небольшую текстуру:



Заменим код в Game.java:

    @Override
    public void simpleInitApp() {
        Box b = new Box(1, 1, 1);
        Geometry geom = new Geometry("Box", b);

        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        TextureKey key = new TextureKey("Textures/poster.jpg");
        Texture tex = assetManager.loadTexture(key);
        mat.setTexture("ColorMap", tex);
        geom.setMaterial(mat);

        rootNode.attachChild(geom);
    }

Запуск приложения на андроид-устройстве должен пройти успешно, в то время как при запуске десктоп-версии ЭВМ выругается злым красным AssetNotFoundException.

Отправляемся в desktop/build.gradle и объясняем:

apply plugin: "java"

sourceSets.main.java.srcDirs = ["src/"]

dependencies {
    compile files("../app/assets")
}

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



Что дальше?


Если вы не очень хорошо знакомы с jME, то неплохо будет отправиться в раздел Tutorials for Beginners на официальном сайте и пройтись по основам. Или даже скачать сам движок в комплекте с IDE на базе NetBeans, JDK, Блендером и всякими другими полезными штуками.

При изучении примеров в корневой build.gradle рекомендую добавить:

project(":core") {
    apply plugin: "java"
    dependencies {
        compile 'com.jme3:jme3-core:3.0.+'

        compile 'com.jme3:jme3-effects:3.0.+'
        compile 'com.jme3:jme3-networking:3.0.+'
        compile 'com.jme3:jme3-plugins:3.0.+'
        compile 'com.jme3:jme3-jogg:3.0.+'
        compile 'com.jme3:jme3-terrain:3.0.+'
        compile 'com.jme3:jme3-blender:3.0.+'
        compile 'com.jme3:jme3-jbullet:3.0.+'
        compile 'com.jme3:jme3-niftygui:3.0.+'

        compile 'net.sf.sociaal:jME3-testdata:3.0.0.20130526'
    }
}

Это почти все модули движка плюс ресурсы для примеров (testdata). Полный список модулей на официальном сайте.

При разработке под мобильные платформы есть определённые сложности (применение шейдеров, обработка жестов, ...), но что-нибудь вполне играбельное можно смастерить если следовать известному крылатому выражению 1899 года. Хотя, наверное, это относится не только к jMonkeyEngine.

Надеюсь, всё это кому-нибудь поможет и никому не навредит.
Теги:
Хабы:
+13
Комментарии 1
Комментарии Комментарии 1

Публикации

Истории

Работа

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн