Creating Android plugins

简介

在移动游戏货币化出现之前,制作便携式视频游戏一切都很好。

这个区域很复杂,通常是一个移动游戏,货币化需要与服务器的特殊连接,例如:

  • 分析
  • 应用内购买
  • 收据验证
  • 安装跟踪
  • 广告
  • 视频广告
  • 交叉推广
  • 游戏中的软硬货币
  • 促销代码
  • A / B测试
  • 登录
  • 云保存
  • 排行榜和分数
  • 用户支持和反馈
  • 发布到Facebook,Twitter等。
  • 推送通知

On iOS, you can write a C++ module and take advantage of the C++/ObjC intercommunication. Even using GDNative is possible to make it a plug-in.

On Android, interfacing with C++ through JNI (Java Native Interface) isn’t as flexible, so writing plugins is considerably more work.

It is also possible that you just want to do modifications to the Android export template, and by using a plugin your project can remain compatible with newer Godot versions (as the android source template will get updated on each release).

Maybe REST

Most of these APIs allow communication via REST/JSON APIs. If the API is relatively simple and does not require complex authentication, this may be a better idea than writing a specific Android plugin.

Godot has great support for HTTP, HTTPS and JSON, so an API implemented this way will work on every platform, too.

Of course, in most of the cases, it’s easier to just write an Android plugin, so keep reading.

Android plugin

Writing an Android plugin is now possible, beginning with Godot 3.2. It’s also pretty easy! Re-compiling the engine is no longer needed.

Before anything, make sure you understand how to set up a custom build environment for Android.

Your plugin needs to be in a folder other than “build/” inside the “res://android” directory (which was created by following the link above). Any name is fine, so name it according to the SDK you will implement (or just your plugin name).

Once created, there are certain rules to follow, but they are simple.

Android directories

Inside your plugin folder, you can use the standard folders as if they were from an Android Gradle project. Examples of this are:

src/ - For Java source code, same as in your Android project
res/ - For resources
aidl/ - For interfaces
assets/ - For assets that will be included as-is on export
libs/debug - For debug JNI libraries
libs/release - For release JNI libraries

Gradle will treat them as part of the project automatically when building, same as the default project files.

“Chunk” files

It is now possible to modify “AndroidManifest.xml” and build.gradle in “res://android/build” directly and Godot will keep your changes when building. The problem, however, is that if you update Godot, you will also need to update the build/ folder and your changes will be lost.

To overcome this, the Godot Android Plugin system lets you create chunk files, where you can specify little bits that can be inserted in both “AndroidManifest.xml” and build.gradle. They are inserted every time Godot builds the project for export or deploy.

AndroidManifest.conf

This file allows to insert bits of chunk into AndroidManifest.xml, the following are supported tags and are entirely optional:

[user_permissions]

Any bit of text below this tag is inserted inside the <manifest> tag of the file. This is often used for permission tags.

[application]

Any bit of text below this tag inside the <application> tag of the file. Many SDKs require this.

[application_attribs]

These are attributes you can add at the end of the <application> tag. Some SDKs require this.

gradle.conf

This file allows to insert bits of chunk into build.gradle, the following are supported and are entirely optional:

[buildscript_repositories]

Any bit of text below this tag is inserted inside the buildscript.repositories section of the build file.

[buildscript_dependencies]

Any bit of text below this tag is inserted inside the buildscript.dependencies section of the build file.

[allprojects_repositories]

Any bit of text below this tag is inserted inside the allprojects.repositories section of the build file.

[dependencies]

Any bit of text below this tag is inserted inside the dependencies section of the build file.

[android_defaultconfig]

Any bit of text below this tag is inserted inside the android.defaultconfig section of the build file.

[global]

Any bit of text below this tag is inserted inside the global scope of the build file.

Java单例

An Android plugin will usually have a singleton class that will load it, this class inherits from Godot.SingletonBase. Resource identifiers for any additional resources you have provided for the module will be in the com.godot.game.R class, so you’ll likely want to import it.

单例对象模板如下:

package org.godotengine.godot;

import android.app.Activity;
import android.content.Intent;
import android.content.Context;
import com.godot.game.R;
import javax.microedition.khronos.opengles.GL10;

public class MySingleton extends Godot.SingletonBase {

    protected Activity appActivity;
    protected Context appContext;
    private Godot activity = null;
    private int instanceId = 0;

    public String myFunction(String p_str) {
        // A function to bind.
        return "Hello " + p_str;
    }

    public void getInstanceId(int pInstanceId) {
        // You will need to call this method from Godot and pass in the get_instance_id().
        instanceId = pInstanceId;
    }

    static public Godot.SingletonBase initialize(Activity p_activity) {
        return new MySingleton(p_activity);
    }

    public MySingleton(Activity p_activity) {
        // Register class name and functions to bind.
        registerClass("MySingleton", new String[]
            {
                "myFunction",
                "getInstanceId"
            });
        this.appActivity = p_activity;
        this.appContext = appActivity.getApplicationContext();
        // You might want to try initializing your singleton here, but android
        // threads are weird and this runs in another thread, so to interact with Godot you usually have to do.
        this.activity = (Godot)p_activity;
        this.activity.runOnUiThread(new Runnable() {
                public void run() {
                    // Useful way to get config info from "project.godot".
                    String key = GodotLib.getGlobal("plugin/api_key");
                    // SDK.initializeHere();
                }
        });

    }

    // Forwarded callbacks you can reimplement, as SDKs often need them.

    protected void onMainActivityResult(int requestCode, int resultCode, Intent data) {}
    protected void onMainRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {}

    protected void onMainPause() {}
    protected void onMainResume() {}
    protected void onMainDestroy() {}

    protected void onGLDrawFrame(GL10 gl) {}
    protected void onGLSurfaceChanged(GL10 gl, int width, int height) {} // Singletons will always miss first 'onGLSurfaceChanged' call.

}

Calling back to Godot

从Java回调Godot有点困难。 首先必须知道脚本的实例ID,这是通过在脚本上调用 get_instance_ID() 获得的。 这将返回一个可以传递给Java的整数。

从Java中,使用 calldeferred 函数与Godot进行通信。 Java很可能在一个单独的线程中运行,因此调用是延迟的:

GodotLib.calldeferred(<instanceid>, "<function>", new Object[]{param1, param2, etc});

Godot will detect this singleton and initialize it at the proper time.

Using it from GDScript

First you will need to add your singleton into the android modules to be loaded. Go to “Project > Project Settings”. Then on the tab “General” go to the “Android” section, and fill the Modules part with your module name. The module should include the full Java path. For our example: org/godotengine/godot/MySingleton.

../../../_images/android_modules.png

Then, from your script:

if Engine.has_singleton("MySingleton"):
    var singleton = Engine.get_singleton("MySingleton")
    print(singleton.myFunction("World"))

故障排除

Godot在装载时坠毁

检查``adb logcat``是否存在可能的问题,然后:

  • 确保libgodot_android.so位于``libs / armeabi``文件夹中
  • 检查Java单例中使用的方法是否仅使用简单的Java数据类型,不支持更复杂的数据类型。

未来

Godot有一个实验性的Java API Wrapper,允许使用GDScript的整个Java API。

它使用起来很简单,就像这样:

class = JavaClassWrapper.wrap(<javaclass as text>)

This is most likely not functional yet, if you want to test it and help us make it work, contact us on irc.freenode.org:#godotengine-devel.