Skip to content

Android

Hajime Hoshi edited this page Sep 23, 2022 · 26 revisions

This document describes about a deprecated feature. Use ebitenmobile command instead. See https://ebitengine.org/en/documents/mobile.html


See the document Mobile for details common in Android and iOS.

In this article, we explain gomobile bind usage, not gomobile build usage.

How to build an Android application

Create a package for mobiles

See https://github.com/hajimehoshi/ebiten/wiki/Mobile#create-a-package-for-mobiles.

Compile the package for mobile

Use gomobile bind, generate an aar file and import this to your Android Studio project as an external aar file. Here is the example command to build the package.

:; gomobile bind -target android -javapkg com.example.yourgame -o /path/to/android/studio/project/mobile.aar github.com/yourname/yourgame/mobile

Implement Java classes

The below implementation implements a GLSurfaceView class and its Renderer class. When the view's onLayout is called, the size of view is calculated and the game starts if the game doesn't start yet.

For an actual example, see go-inovation's EbitenGLSurfaceView.java.

package com.example.yourgame;

import android.content.Context;
import android.opengl.GLSurfaceView;
import android.util.Log;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import com.hajimehoshi.goinovation.mobile.*;

public class EbitenGLSurfaceView extends GLSurfaceView {

    private class EbitenRenderer implements Renderer {

        private boolean mErrored;

        @Override
        public void onDrawFrame(GL10 gl) {
            if (mErrored) {
                return;
            }
            try {
                // onDrawFrame is called every frame.
                // Let's call your game's Update.
                Mobile.Update();
            } catch (Exception e) {
                Log.e("Go Error", e.toString());
                mErrored = true;
            }
        }

        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        }

        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
        }
    }

    private double mDeviceScale = 0.0;

    public EbitenGLSurfaceView(Context context) {
        super(context);
        initialize();
    }

    public EbitenGLSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initialize();
    }

    private void initialize() {
        setEGLContextClientVersion(2);
        setEGLConfigChooser(8, 8, 8, 8, 0, 0);
        setRenderer(new EbitenRenderer());
    }

    // pxToDp converts an value in pixels to dp.
    // Note that Ebiten's mobile.Start accepts size value in dp.
    private double pxToDp(double x) {
        if (mDeviceScale == 0.0) {
            mDeviceScale = getResources().getDisplayMetrics().density;
        }
        return x / mDeviceScale;
    }

    // getScaleInPx returns a scale value which fits the parent view.
    public double getScaleInPx() {
        View parent = (View)getParent();
        return Math.max(1,
                Math.min(parent.getWidth() / (double)Mobile.ScreenWidth,
                        parent.getHeight() / (double)Mobile.ScreenHeight));
    }

    @Override
    public void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        // Calculate the scale fitting the screen and use it.
        double scaleInPx = getScaleInPx();
        getLayoutParams().width = (int)(Mobile.ScreenWidth * scaleInPx);
        getLayoutParams().height = (int)(Mobile.ScreenHeight * scaleInPx);
        try {
            if (!Mobile.IsRunning()) {
                Mobile.Start(pxToDp(scaleInPx));
            }
        } catch (Exception e) {
            Log.e("Go Error", e.toString());
        }
        // If you want to center this view, you can set `android:layout_centerHorizontal="true"` and
        // `android:layout_centerVertical="true"` in the layout XML.
    }

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        for (int i = 0; i < e.getPointerCount(); i++) {
            int id = e.getPointerId(i);
            int x = (int)e.getX(i);
            int y = (int)e.getY(i);
            // Call `mobile.UpdateTouchesOnAndroid` via your exported function.
            // The position is in dp.
            Mobile.UpdateTouchesOnAndroid(e.getActionMasked(), id, (int)pxToDp(x), (int)pxToDp(y));
        }
        return true;
    }
}

Note that Seq.setContext must be called when the activity is created.

// ...

import go.Seq;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Seq.setContext(getApplicationContext());
    }

    // ...

}