Updated Android (markdown)

Hajime Hoshi 2016-07-08 02:04:42 +09:00
parent 9db3edfa15
commit 2923732ace

@ -1,5 +1,151 @@
Ebiten game can be built for Android by using [`gomobile bind`](golang.org/x/mobile/cmd/gomobile), which generates an aar shared library file. Note that Ebiten doesn't accept `gomobile build`, which generates an apk application file. Why Ebiten only uses `gomobile bind` is because `gomobile build` is difficult to build an actual application in Play store. `gomobile build` is very easy to use to generate an application written only in Go, but the application includes only one OpenGL surface. Even if you want to an advertisement or use inputting via the keyboard, it is almost impossible. On the other hand, `gomobile bind` generates a shared library and you'd need some glue Java code to use this, but that offers flexibility where you can do everything.
# Actual example
[Inovation 2007](https://github.com/hajimehoshi/go-inovation) is an actual Android game using Ebiten. See [the Android Studio project](https://github.com/hajimehoshi/go-inovation/tree/master/mobile/android). This application has been released in [Play store](https://play.google.com/store/apps/details?id=com.hajimehoshi.goinovation).
# How to build Android application
(TBD)
## Create a module for mobiles
You need to create a module for mobiles with `github.com/hajimehoshi/ebiten/mobile` for your game. This module has exported functions to Java side.
For an actual example, see [`github.com/hajimehoshi/go-inovation/mobile/mobile.go`](https://github.com/hajimehoshi/go-inovation/blob/master/mobile/mobile.go).
```go
package inovation
import (
"github.com/hajimehoshi/ebiten/mobile"
"github.com/yourname/yourgame"
)
var (
running bool
)
const (
ScreenWidth = 320
ScreenHeight = 240
)
// IsRunning returns a boolean value indicating whether the game is running.
func IsRunning() bool {
return running
}
// Start starts the game.
func Start(scale float64) error {
running = true
game, err := yourgame.NewGame()
if err != nil {
return err
}
// mobile.Start starts the game.
// In this function, scale is passed from Java side and pass it to mobile.Start.
// You can also receive the screen size from Java side by adding arguments to this function
// if you want to make Java side decide the screen size.
// Note that the screen size unit is dp (device-independent pixel).
if err := mobile.Start(game.Update, ScreenWidth, ScreenHeight, scale, "Your Game's Title"); err != nil {
return err
}
return nil
}
// Update proceeds the game.
func Update() error {
return mobile.Update()
}
// UpdateTouchesOnAndroid dispatches touch events.
func UpdateTouchesOnAndroid(action int, id int, x, y int) {
mobile.UpdateTouchesOnAndroid(action, id, x, y)
}
func UpdateTouchesOnIOS(phase int, ptr int64, x, y int) {
// Prepare this function if you also want to make your game run on iOS.
mobile.UpdateTouchesOnIOS(phase, ptr, x, y)
}
```
## Compile the module 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 module.
```sh
:; :; gomobile bind -target android -javapkg com.example.yourgame -o /path/to/android/studio/project/mobile.aar github.com/yourname/yourgame/mobile
```
## Implement a class inheriting GLSurfaceView
```java
// ...
import com.example.yourgame
// ...
public class EbitenGLSurfaceView extends GLSurfaceView {
// ...
private double mDeviceScale = 0.0;
// 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)Yourgame.ScreenWidth,
parent.getHeight() / (double)Yourgame.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.
// If you want to center this view, you can set `android:layout_centerHorizontal="true"` and
// `android:layout_centerVertical="true"` in the layout XML.
double scaleInPx = getScaleInPx();
getLayoutParams().width = (int)(Yourgame.ScreenWidth * scaleInPx);
getLayoutParams().height = (int)(Yourgame.ScreenHeight * scaleInPx);
try {
// onLayout might be called multiple times. Don't call Start if the game is already running.
if (!Yourgame.IsRunning()) {
// Be careful that Ebiten accepts the scale in dp, not in px.
Yourgame.Start(pxToDp(scaleInPx));
}
} catch (Exception e) {
Log.e("Go Error", e.toString());
}
}
}
```
## Dispatch touch events
Call `mobile.UpdateTouchesOnAndroid` via your exported function.
```java
public class EbitenGLSurfaceView extends GLSurfaceView {
// ...
@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);
Inovation.UpdateTouchesOnAndroid(e.getActionMasked(), id, (int)pxToDp(x), (int)pxToDp(y));
}
return true;
}
}
```