JNISingleton in Godot – Complete Guide

Stepping into the world of game development introduces you to a myriad of tools and techniques that can transform your creative ideas into playable realities. Among these tools, Godot Engine stands as a powerful, open-source platform known for its ease of use and versatility. If you’re diving into mobile game development, especially targeting the Android ecosystem, you’re bound to encounter the need for extending your game’s capabilities through native code integration. That’s where Godot’s JNISingleton class comes into play.

What is JNISingleton?

Understanding JNISingleton

The JNISingleton class in Godot Engine is a gateway to the Android world, allowing developers to seamlessly call methods and connect signals from Android plugins directly into their Godot projects. Utilizing the Java Native Interface or JNI, this class serves as the critical link between your game and Android’s native functionalities.

Enhancing Your Game with JNISingleton

Why would you want to reach out to Android’s native environment? The reasons are plentiful. From accessing device sensors, implementing in-app purchases, ads, or analytics, to integrating third-party SDKs, the JNISingleton enables your game to embrace the full spectrum of features provided by the Android platform.

Why Should I Learn to Use JNISingleton?

Embracing the JNISingleton class empowers you with the capability to tailor an immersive gaming experience specific to Android users. By learning JNISingleton, you’re not just enhancing your game; you’re expanding your toolkit as a Godot developer, enabling you to create more polished, feature-rich mobile games that stand out in the crowded app marketplaces.

Moving forward, our tutorial will delve into the practical applications of the JNISingleton class. We’ll start with the basics and gradually build up to more complex examples, ensuring that whether you’re a budding developer or a seasoned programmer, you’ll find valuable insights in harnessing this powerful feature in Godot 4. Stay tuned as we explore the exciting world of Android integration with Godot!

CTA Small Image
FREE COURSES AT ZENVA
LEARN GAME DEVELOPMENT, PYTHON AND MORE
ACCESS FOR FREE
AVAILABLE FOR A LIMITED TIME ONLY

Setting Up Your Godot Project for Android

To kick things off, you need to ensure that your Godot project is set up correctly to build for Android. This involves configuring the export settings and ensuring you have the Android SDK properly installed. Let’s go through how to prepare your Godot project:

# Initializes the Android environment for your project
var instance = Engine.get_singleton("Godot")

# Add your project-specific configuration code here..

Note the use of Engine.get_singleton(“Godot”); this is how you access the JNISingleton instance for calling Android methods.

Creating Your First JNISingleton Class

Creating a JNISingleton involves extending the Android Java Class, `Godot.SingletonBase`. Here’s a straightforward example to illustrate:

// MyPlugin.java
package org.godotengine.godot;

public class MyPlugin extends Godot.SingletonBase {

    protected void onMainActivityResult(int requestCode, int resultCode, Intent data) {
        // Handle activity result
    }

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

    public MyPlugin(Activity p_activity) {
        // Register class name and functions to bind.
        registerClass("MyPlugin", new String[]{
            "myFunction"
        });
    }

    public void myFunction() {
        // Your custom function implementation
    }
}

In the example above, we have created a new plugin called “MyPlugin” that extends Godot’s SingletonBase. Upon initializing, it registers a function called myFunction, which can be called from Godot.

Calling a Java Function from GDScript

Once you have JNISingleton set up, it’s time to call the Java functions from GDScript. Here’s an example of calling the myFunction that we defined in our plugin:

# Assuming the instance has been set up as shown earlier
instance.myFunction()

This GDScript snippet calls the myFunction from the Java side. Remember, the function names are case sensitive and must match exactly as they are in the Java code.

Implementing Custom Android Functions

Let’s expand on what we can do by adding more custom functions to our plugin:

// Adding a method with parameters
public void showToast(final String message, final int duration) {
    activity.runOnUiThread(new Runnable() {
        public void run() {
            Toast.makeText(activity, message, duration).show();
        }
    });
}

And now, calling that function from GDScript would look something like this:

# Calls the showToast method
instance.showToast("Hello from Godot!", Toast.LENGTH_SHORT)

Note that in order to interact with the UI Thread from Java, we use activity.runOnUiThread, as Android UI operations need to run on the main thread.

Handling Callbacks from Java to GDScript

Sometimes you want to execute a callback function in Godot when a long-running Java operation completes. First, let’s define a signal in GDScript:

signal java_callback_received(args)

# Connect the signal in your GDScript code
instance.connect("java_callback_received", self, "_on_java_callback_received")

func _on_java_callback_received(args):
    print("Callback received from Java: %s" % args)

Next, in Java, we call back to GDScript using Godot’s `emitSignal` function:

// Java side
public void sendCallbackToGDScript(final String result) {
    GodotLib.calldeferred(instance_id, "emit_signal", new Object[]{"java_callback_received", result});
}

In this example, we are hooking up a callback to handle a signal emitted from Java. The signal java_callback_received will be emitted with any arguments sent from Java side whenever the related event occurs.

All these examples lay the groundwork for manipulating the powerful interplay between Godot and Android. In our subsequent sections, we will dive deeper into utilizing these techniques to create more complex and interactive game features. Stay tuned for more insights into bringing your Android game to life with Godot!

The real strength of using JNISingleton in Godot is that it opens doors to the myriad features and native capabilities of Android devices. Let’s advance our exploration with practical examples that game developers frequently encounter and can implement through JNISingleton.

Accessing Device Hardware Information

Often, games need to adapt to various hardware specifications or utilize specific device features. Here’s how you might access device hardware information using JNISingleton:

// Java side: A method that returns the device model
public String getDeviceModel() {
    return android.os.Build.MODEL;
}

In your GDScript, you would call this method to get the device model:

var model = instance.getDeviceModel()
print("Running on device: " + model)

This function can help you tailor your game performance to the device’s capabilities.

Integrating Android Intents

Android’s Intent system allows you to perform a variety of actions, such as opening a web page in a browser. Here’s how you might launch the default web browser to a specified URL:

// Java side: A method to launch a web URL
public void launchWebURL(String url) {
    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
    activity.startActivity(intent);
}

And in Godot, you could use this function as follows:

instance.launchWebURL("https://www.example.com")

Through this integration, you can link players to websites, such as your game’s community forum or support page.

Implementing Social Sharing

It’s common to want players to share game achievements or content. You can facilitate this by implementing a social sharing feature:

// Java side: Sharing text content
public void shareTextContent(String subject, String text) {
    Intent sendIntent = new Intent();
    sendIntent.setAction(Intent.ACTION_SEND);
    sendIntent.putExtra(Intent.EXTRA_TEXT, text);
    sendIntent.setType("text/plain");

    Intent shareIntent = Intent.createChooser(sendIntent, subject);
    activity.startActivity(shareIntent);
}

In GDScript, when a player hits the share button, you’d trigger:

instance.shareTextContent("Check out my score!", "I scored 9000 points on SuperGame!")

This lets players spread the word about your game, creating a natural word-of-mouth marketing channel.

Check Network Connectivity

Checking for network connectivity can be important, especially if your game requires an internet connection:

// Java side: A method to check network connectivity
public boolean isNetworkAvailable() {
    ConnectivityManager cm = (ConnectivityManager)activity.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
    return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
}

Then in Godot, you might handle this check before starting an online game:

if instance.isNetworkAvailable():
    print("Network is available, start online game")
else:
    print("No network, offer offline mode")

By correctly reacting to the network state, you can ensure a better user experience.

Adjusting Screen Brightness

Let’s explore managing another common device feature: screen brightness. Here’s a simple way to set the screen brightness from within your game:

// Java side: Set screen brightness
public void setScreenBrightness(int value) {
    android.view.Window window = activity.getWindow();
    android.view.WindowManager.LayoutParams layout = window.getAttributes();
    layout.screenBrightness = value / 100.0f;
    window.setAttributes(layout);
}

And in GDScript, you could provide a setting in your game preferences for screen brightness management:

func _on_brightness_slider_value_changed(value):
    instance.setScreenBrightness(value)

This example illustrates just the tip of the iceberg when it comes to interfacing with Android’s native APIs through Godot’s JNISingleton class. It capitalizes on the user’s familiarity with their device features, adding another layer of personalization and polish to your game.

As we continue to investigate this junction of Godot and Android, your capacity to invent experiences beyond the confines of the engine grows. It’s essential to remember that with JNISingleton, virtually any feature or information accessible on Android can be incorporated into your Godot project, springboarding your game into a league where it not merely operates but thrives on the Android platform.

As we move forward with our exploration of Godot’s JNISingleton and Android, let’s examine additional practical examples that you, as a game developer, can implement to leverage Android’s robust feature set.

Incorporating Vibration

Adding a tactile dimension to your game can immensely enhance player immersion. Below is how you might integrate device vibration:

// Java side: Vibrate for a specific duration
@SuppressLint("MissingPermission") // Ensure you have vibration permissions
public void vibrateDevice(long milliseconds) {
    Vibrator vibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE);
    if (vibrator.hasVibrator()) {
        vibrator.vibrate(milliseconds);
    }
}

To trigger a vibration from GDScript after an in-game event:

instance.vibrateDevice(500) # Vibrate for 0.5 seconds

Remember that starting from Android API 26 (Android 8.0), you need to handle vibrations using the VibrationEffect class.

Using the Android Camera

You might want to integrate the phone’s camera to let players add a personal touch to their game avatars or for augmented reality features:

// Java side: Launch camera intent
public void openCamera() {
    Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE)
    activity.startActivityForResult(cameraIntent, CAMERA_REQUEST_CODE)
}

// Override this method to handle the camera result
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == CAMERA_REQUEST_CODE && resultCode == RESULT_OK) {
        Bundle extras = data.getExtras()
        Bitmap imageBitmap = (Bitmap) extras.get("data")
        // Handle the image returned from camera
    }
}

On the GDScript side:

instance.openCamera()

After taking a picture, you could handle the image data on the Java side or pass it back to Godot to be used in the game.

Managing Game Saves with Android Storage Access

Game data persistence is crucial, and you may need to work with Android’s file system for saving or loading game states:

// Java side: Write to a file in the external storage
public void writeToFile(String fileName, String content) {
    try {
        File externalStoragePublicDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
        File file = new File(externalStoragePublicDirectory, fileName);
        FileWriter fileWriter = new FileWriter(file);
        fileWriter.append(content);
        fileWriter.flush();
        fileWriter.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

To write game save data from GDScript:

instance.writeToFile("save_game.txt", "save data contents")

Ensure that your app has the required permissions to access the file system on the device. Starting with Android 10 (API level 29), consider scoped storage guidelines.

Listening to Battery State

It’s good to be aware of the device’s battery state—games could adjust their features based on this to conserve battery or warn users of low power:

// Java side: Broadcast receiver for battery changes
private BroadcastReceiver batteryLevelReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
        // You can emit signal to GDScript here or handle it as required
    }
};

public void startBatteryLevelMonitoring() {
    IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
    activity.registerReceiver(batteryLevelReceiver, ifilter);
}

Commence monitoring from Godot like this:

instance.startBatteryLevelMonitoring()

Always opt for unregistering receivers when not needed to prevent memory leaks.

Configuring Fullscreen Modes

Maximizing screen real estate is essential for many games. Here’s how you might control the full-screen configuration:

// Java side: Enable fullscreen mode
public void enableFullscreen() {
    activity.runOnUiThread(new Runnable() {
        public void run() {
            View decorView = activity.getWindow().getDecorView();
            decorView.setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_FULLSCREEN
                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
        }
    });
}

To enter the fullscreen mode from within Godot:

instance.enableFullscreen()

Such native Android integrations through JNISingleton reinforce that your Godot game is not just a game but a part of the device’s ecosystem, maximizing Android’s features for a full-featured gaming experience. We are constantly expanding our abilities and knowledge as developers, and Godot’s JNISingleton class is a potent means to bring your games that competitive edge. Continue your journey on Zenva, as we provide more tutorials and content to help you become an expert in Godot, game development, and more. Embrace the learning curve, and soon you’ll be crafting experiences that resonate with players and keep them coming back for more.

Continuing Your Godot Development Journey

The road to mastering game development with Godot doesn’t end here! With the skills you’ve gained, a universe of possibilities just opened up for you. To keep the momentum going and to explore in greater depth the power of Godot, we invite you to check out our Godot Game Development Mini-Degree. This comprehensive selection of courses will guide you through everything from the basics to more advanced concepts, ensuring a well-rounded understanding of game design and development.

Whether you’re a complete beginner or looking to sharpen your skills further, our Mini-Degree will support your learning at every step. You’ll build a range of games, learn to manage complex gameplay systems, and construct rich, interactive environments all using the Godot Engine. And the best part? You can learn entirely at your own pace, gradually building a professional portfolio that could kickstart or enhance your career in game development.

If you’re eager to expand your knowledge even further, we also offer a wider array of Godot courses tailored to fit your specific interests and goals. From mobile game mechanics to multiplayer integrations, Zenva is dedicated to providing you with the skills needed to turn your game development aspirations into tangible creations. Continue your learning journey with us, and bring your game development dreams to life!

Conclusion

As we wrap up this exploration of Godot’s JNISingleton class for Android, remember that the skills you’ve acquired are just the beginning. By harnessing the power of Godot in combination with native Android features, you’re well on your way to creating enthralling mobile games that fully engage players on their favorite devices. Continue to experiment, build, and iterate on your creations, because every step you take is a leap towards mastering game development.

Don’t stop here! Dive deeper into the rich capabilities that Godot offers by enrolling in our Godot Game Development Mini-Degree. Let your curiosity and passion for game design steer you towards new heights, and empower yourself with the knowledge to craft the next hit game. Join us at Zenva, where learning never ends and where your next big game idea can come to life.

FREE COURSES
Python Blog Image

FINAL DAYS: Unlock coding courses in Unity, Godot, Unreal, Python and more.