Mobile
TL;DR
Use ebitenmobile
command to create a shared library. The generated library includes a complete and easy-to-use view (or view controller) class.
Creating a shared library with ebitenmobile bind
Installing ebitenmobile
Install ebitenmobile
command first.
go install github.com/hajimehoshi/ebiten/v2/cmd/ebitenmobile@latest
Creating a binding
Create a package for mobiles to use github.com/hajimehoshi/ebiten/v2/mobile
:
package yourgamemobile
import (
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/mobile"
"github.com/yourname/yourgame"
)
func init() {
// yourgame.Game must implement ebiten.Game interface.
// For more details, see
// * https://pkg.go.dev/github.com/hajimehoshi/ebiten/v2#Game
mobile.SetGame(&yourgame.Game{})
}
// Dummy is a dummy exported function.
//
// gomobile doesn't compile a package that doesn't include any exported function.
// Dummy forces gomobile to compile this package.
func Dummy() {}
The key function is SetGame
. Do not call RunGame
, which you usually use for desktop games.
Android
The minimum required Android SDK version is 16.
Set up these environment variables:
# macOS
export ANDROID_HOME=~/Library/Android/sdk
export PATH=/Applications/Android\ Studio.app/Contents/jbr/Contents/Home/bin:$PATH
(TODO: Environment variables for Windows)
Run ebitenmobile bind
command for your package:
cd /path/to/yourgame
ebitenmobile bind -target android -javapkg your.package.name -o path/to/yourgame.aar .
Then your can get a .aar
file. This .aar
file defines a view class named EbitenView
under the specified Java package (-javapkg
option + your package name). You can put it on your screen. That's it!
The view class is defined like this:
package your.package.name.yourgamemobile;
import android.view.ViewGroup;
public class EbitenView extends ViewGroup {
// onErrorOnGameUpdate is called on the main thread when an error happens when updating a game.
// You can define your own error handler, e.g., using Crashlytics, by overwriting this method.
protected void onErrorOnGameUpdate(Exception e) {
// Default error handling implementation.
}
// suspendGame suspends the game.
// It is recommended to call this when the application is being suspended e.g.,
// Activity's onPause is called.
public void suspendGame() {
// ...
}
// resumeGame resumes the game.
// It is recommended to call this when the application is being resumed e.g.,
// Activity's onResume is called.
public void resumeGame() {
// ...
}
}
Note that you have to call Seq.setContext
at the Activity creation like this:
package your.package.name.yourgamemobile;
import androidx.appcompat.app.AppCompatActivity;
import go.Seq;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Seq.setContext(getApplicationContext());
// ...
}
}
If you want to use multiple Activity
objects or Intent
, please consider to make the Activity
for Ebitengine singleInstance
. It's because Ebitengine's state might be broken when recreating an activity. You can set this in your AndroidManifest.xml
like the following.
<activity
...
android:launchMode="singleInstance">
...
</activity>
iOS
The minimum required iOS version is 13.0.
Run ebitenmobile bind
command for your package:
cd /path/to/yourgame
ebitenmobile bind -target ios -o path/to/YourGame.xcframework .
This command generates a .xcframework
file. This .xcframework
defines a view controller class named (Package Name)EbitenViewController
.
In order to use this framework at a new Xcode project, you can choose either of these options:
- Changing the parent class of the default
ViewController
fromUIViewController
to(Package Name)EbitenViewController
. - Defining your own
ViewController
class inheriting(Package Name)EbitenViewController
and setting it as the custom class of the view controller in the main storyboard (Main.storyboard
).
In both ways, you would have to import your game's framework like this to use (Package Name)EbitenViewController
.
#import <YourGame/YourGame.h>
The view controller class in the generated xcframework is defined like this:
#import <UIKit/UIKit.h>
@interface YourgamemobileEbitenViewController : UIViewController
// onErrorOnGameUpdate is called on the main thread when an error happens when updating a game.
// You can define your own error handler, e.g., using Crashlytics, by overwriting this method.
- (void)onErrorOnGameUpdate:(NSError*)err;
// suspendGame suspends the game.
// It is recommended to call this when the application is being suspended e.g.,
// UIApplicationDelegate's applicationWillResignActive is called.
- (void)suspendGame;
// resumeGame resumes the game.
// It is recommended to call this when the application is being resumed e.g.,
// UIApplicationDelegate's applicationDidBecomeActive is called.
- (void)resumeGame;
@end
Dependencies
An Ebitengine view controller might require these dependencies:
- GameController.framework
Example
go-inovation is a complete example to use ebitenmobile bind
.
Why ebitenmobile bind
?
ebitenmobile bind
requires less boilerplate code thangomobile bind
.ebitenmobile bind
's views hide implementation details completely. Now Android uses OpenGL ES and iOS uses Metal, and this fact is hid byebitenmobile
. Android might migrate to Vulkan in the future. Even when migration happens, you don't have to care anything as long as you useebitenmobile bind
.
Why not a complete application instead of a shared library?
It is because it is not feasible to write a mobile application only in Go yet.
- You'd need to access native APIs to access many features.
- It is very common to add a native component (e.g., ad banners) to your application.
- To launch your application on the stores, you'd need to insert certifications or other files. Android Studio or Xcode care them. It'd be really hard for the IDEs to treat a complete application generated by other tools.